Code

added lpe-dynastroke, but hidden as experimental.
authorjfbarraud <jfbarraud@users.sourceforge.net>
Sat, 3 Jan 2009 01:40:34 +0000 (01:40 +0000)
committerjfbarraud <jfbarraud@users.sourceforge.net>
Sat, 3 Jan 2009 01:40:34 +0000 (01:40 +0000)
src/live_effects/CMakeLists.txt
src/live_effects/effect-enum.h
src/live_effects/effect.cpp
src/live_effects/lpe-dynastroke.cpp [new file with mode: 0644]
src/live_effects/lpe-dynastroke.h [new file with mode: 0644]

index 72d955586b8bc9ab023f78ea7d2a7790e69af1c1..583386796e65c05ba4f007dba55e43ff50af68d4 100644 (file)
@@ -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
 )
index 6409fecd251679ad83fe47b5d6309b16efd00064..b6a091b0cfc71c82d957f3a0ca2d91e67e9b88dd 100644 (file)
@@ -48,6 +48,7 @@ enum EffectType {
     LINE_SEGMENT,
 #ifdef LPE_ENABLE_TEST_EFFECTS
     DOEFFECTSTACK_TEST,
+    DYNASTROKE,
 #endif
     INVALID_LPE // This must be last
 };
index dc252722abd28503b828ebf93d8a097ca2610d78..83e3ccb8db7bf835187fd5c42d06c0bfaff7ace7 100644 (file)
@@ -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<EffectType> 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<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
             break;
+        case DYNASTROKE:
+            neweffect = static_cast<Effect*> ( 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 (file)
index 0000000..77fc86d
--- /dev/null
@@ -0,0 +1,329 @@
+#define INKSCAPE_LPE_DYNASTROKE_CPP
+/** \file
+ * LPE <dynastroke> implementation
+ */
+/*
+ * Authors:
+ *   JF Barraud
+*
+* Copyright (C) JF Barraud 2007 <jf.barraud@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-dynastroke.h"
+#include "display/curve.h"
+//# include <libnr/n-art-bpath.h>
+
+#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<double> 
+find_corners (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &m){
+    using namespace Geom;
+    std::vector<double> output = std::vector<double>();
+    Piecewise<D2<SBasis> > v = derivative(m);
+    for (unsigned i=0; i<m.size()-1; i++){
+        if ( m.segs[i].at1() == m.segs[i+1].at0() && 
+             v.segs[i].at1() != v.segs[i+1].at0()){
+            output.push_back(m.cuts[i+1]);
+        }
+    }
+    return output;
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+//----------------------------------------------------------
+
+//TODO: growfor/fadefor can be expressed in unit of width.
+//TODO: make round/sharp end choices independant for start and end.
+//TODO: define more styles like in calligtool.
+//TODO: allow fancy ends.
+
+static const Util::EnumData<DynastrokeMethod> 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<DynastrokeMethod> DSMethodConverter(DynastrokeMethodData, DSM_END);
+
+static const Util::EnumData<DynastrokeCappingType> DynastrokeCappingTypeData[DSCT_END] = {
+    {DSCT_SHARP, N_("Sharp"),    "sharp"},
+    {DSCT_ROUND, N_("Round"),    "round"},
+};
+static const Util::EnumDataConverter<DynastrokeCappingType> 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<Parameter *>(& method) );
+    registerParameter( dynamic_cast<Parameter *>(& width) );
+    registerParameter( dynamic_cast<Parameter *>(& roundness) );
+    registerParameter( dynamic_cast<Parameter *>(& angle) );
+    //registerParameter( dynamic_cast<Parameter *>(& modulo_pi) );
+    registerParameter( dynamic_cast<Parameter *>(& start_cap) );
+    registerParameter( dynamic_cast<Parameter *>(& growfor) );
+    registerParameter( dynamic_cast<Parameter *>(& end_cap) );
+    registerParameter( dynamic_cast<Parameter *>(& fadefor) );
+    registerParameter( dynamic_cast<Parameter *>(& round_ends) );
+    registerParameter( dynamic_cast<Parameter *>(& 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<Geom::D2<Geom::SBasis> >
+LPEDynastroke::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
+{
+    using namespace Geom;
+
+     std::cout<<"do effect: debut\n";
+    
+     Piecewise<D2<SBasis> > output;
+     Piecewise<D2<SBasis> > m = pwd2_in;
+     Piecewise<D2<SBasis> > v = derivative(m);;
+     Piecewise<D2<SBasis> > n = unitVector(v);
+     n = rot90(n);
+     Piecewise<D2<SBasis> > n1,n2;
+
+//      for (unsigned i=0; i<n.size(); i++){
+//          std::cout<<n[i][X]<<"\n";
+//      }
+//      return m + unitVector(v);
+
+#if 0
+     Piecewise<SBasis> 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<D2<SBasis> > left = m + k*n;
+     Piecewise<D2<SBasis> > right = m - k*n;
+     right = compose(right,Linear(right.cuts.back(),right.cuts.front()));
+     D2<SBasis> line;
+     line[X] = Linear(left.lastValue()[X],right.firstValue()[X]);
+     line[Y] = Linear(left.lastValue()[Y],right.firstValue()[Y]);
+     output = left;
+     output.concat(Piecewise<D2<SBasis> >(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<D2<SBasis> >(line));
+     return output;
+#else
+
+     double angle_rad = angle*M_PI/180.;//TODO: revert orientation?...
+     Piecewise<SBasis> w;
+     
+     std::vector<double> 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<D2<SBasis> > 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<Piecewise<SBasis> > 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<Piecewise<SBasis> > 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<SBasis> dw = derivative(w);
+                 Piecewise<SBasis> 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<D2<SBasis> > 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<SBasis > 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<SBasis > factor_in = Piecewise<SBasis >(join);
+         factor_in.cuts[1]=grow_length;
+         if (grow_length < totlength){
+             factor_in.concat(Piecewise<SBasis >(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<SBasis > factor_out;
+         if (fade_length < totlength){
+             factor_out = Piecewise<SBasis >(Linear(1));
+             factor_out.cuts[1] = totlength-fade_length;
+             factor_out.concat(Piecewise<SBasis >(join));
+             factor_out.cuts[2] = totlength;
+         }else{
+             factor_out = Piecewise<SBasis >(join);
+             factor_out.setDomain(Interval(totlength-fade_length,totlength));
+         }
+         std::cout<<"shaping the ends ici ici\n";
+         
+         Piecewise<SBasis > 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<SBasis > hbump;
+             hbump.concat(Piecewise<SBasis >(tau*grow_length));
+             hbump.concat(Piecewise<SBasis >(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<SBasis > hbump;
+             hbump.concat(Piecewise<SBasis >(Linear(0)));
+             hbump.concat(Piecewise<SBasis >(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 (file)
index 0000000..ced3765
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef INKSCAPE_LPE_DYNASTROKE_H
+#define INKSCAPE_LPE_DYNASTROKE_H
+
+/** \file
+ * LPE <dynastroke> implementation, see lpe-dynastroke.cpp.
+ */
+
+/*
+ * Authors:
+ *   JFB, but derived from Johan Engelen!
+ *
+ * Copyright (C) JF Barraud 2008 <jf.barraud@gmail.com>
+ *
+ * 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<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
+
+private:
+    EnumParam<DynastrokeMethod> method;
+    ScalarParam width;
+    ScalarParam roundness;
+    ScalarParam angle;
+    //BoolParam modulo_pi;
+    EnumParam<DynastrokeCappingType> start_cap;
+    EnumParam<DynastrokeCappingType> end_cap;
+    ScalarParam growfor;
+    ScalarParam fadefor;
+    BoolParam round_ends;
+    PathParam  capping;
+
+    LPEDynastroke(const LPEDynastroke&);
+    LPEDynastroke& operator=(const LPEDynastroke&);
+};
+
+} //namespace LivePathEffect
+} //namespace Inkscape
+
+#endif