From dc98accfae7a38326b92d74fa4330ac8ccb5b778 Mon Sep 17 00:00:00 2001 From: jfbarraud Date: Mon, 25 Feb 2008 00:04:24 +0000 Subject: [PATCH] 3 new lpe gadgets: sketch, knot and vonkoch. --- src/live_effects/Makefile_insert | 6 + src/live_effects/effect.cpp | 17 ++ src/live_effects/effect.h | 3 + src/live_effects/lpe-knot.cpp | 260 +++++++++++++++++++++++ src/live_effects/lpe-knot.h | 47 +++++ src/live_effects/lpe-sketch.cpp | 347 +++++++++++++++++++++++++++++++ src/live_effects/lpe-sketch.h | 63 ++++++ src/live_effects/lpe-vonkoch.cpp | 158 ++++++++++++++ src/live_effects/lpe-vonkoch.h | 46 ++++ 9 files changed, 947 insertions(+) create mode 100644 src/live_effects/lpe-knot.cpp create mode 100644 src/live_effects/lpe-knot.h create mode 100644 src/live_effects/lpe-sketch.cpp create mode 100644 src/live_effects/lpe-sketch.h create mode 100644 src/live_effects/lpe-vonkoch.cpp create mode 100644 src/live_effects/lpe-vonkoch.h diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 2c9192468..d639bb42d 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -18,6 +18,12 @@ live_effects_liblive_effects_a_SOURCES = \ live_effects/lpe-skeletalstrokes.h \ live_effects/lpe-pathalongpath.cpp \ live_effects/lpe-pathalongpath.h \ + live_effects/lpe-sketch.cpp \ + live_effects/lpe-sketch.h \ + live_effects/lpe-knot.cpp \ + live_effects/lpe-knot.h \ + live_effects/lpe-vonkoch.cpp \ + live_effects/lpe-vonkoch.h \ live_effects/lpe-curvestitch.cpp \ live_effects/lpe-curvestitch.h \ live_effects/lpe-gears.cpp \ diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index bdbc4dd52..777be6e77 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -34,6 +34,10 @@ // include effects: #include "live_effects/lpe-skeletalstrokes.h" #include "live_effects/lpe-pathalongpath.h" +//here!! +#include "live_effects/lpe-sketch.h" +#include "live_effects/lpe-vonkoch.h" +#include "live_effects/lpe-knot.h" #include "live_effects/lpe-slant.h" #include "live_effects/lpe-test-doEffect-stack.h" #include "live_effects/lpe-gears.h" @@ -49,6 +53,9 @@ const Util::EnumData LPETypeData[INVALID_LPE] = { // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"} {PATH_ALONG_PATH, N_("Bend Path"), "bend_path"}, {SKELETAL_STROKES, N_("Pattern Along Path"), "skeletal"}, + {SKETCH, N_("Sketch"), "sketch"}, + {VONKOCH, N_("VonKoch"), "vonkoch"}, + {KNOT, N_("Knot"), "knot"}, #ifdef LPE_ENABLE_TEST_EFFECTS {SLANT, N_("Slant"), "slant"}, {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"}, @@ -69,6 +76,16 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case PATH_ALONG_PATH: neweffect = (Effect*) new LPEPathAlongPath(lpeobj); break; +//here!! + case SKETCH: + neweffect = (Effect*) new LPESketch(lpeobj); + break; + case VONKOCH: + neweffect = (Effect*) new LPEVonKoch(lpeobj); + break; + case KNOT: + neweffect = (Effect*) new LPEKnot(lpeobj); + break; #ifdef LPE_ENABLE_TEST_EFFECTS case SLANT: neweffect = (Effect*) new LPESlant(lpeobj); diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 8354c218b..fb5e85b1b 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -52,6 +52,9 @@ namespace LivePathEffect { enum EffectType { PATH_ALONG_PATH = 0, SKELETAL_STROKES, + SKETCH, + VONKOCH, + KNOT, #ifdef LPE_ENABLE_TEST_EFFECTS SLANT, DOEFFECTSTACK_TEST, diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp new file mode 100644 index 000000000..3e87bc51b --- /dev/null +++ b/src/live_effects/lpe-knot.cpp @@ -0,0 +1,260 @@ +#define INKSCAPE_LPE_KNOT_CPP +/** \file + * LPE implementation + */ +/* + * Authors: + * Johan Engelen +* +* Copyright (C) Johan Engelen 2007 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-knot.h" +#include "display/curve.h" +#include + +// You might need to include other 2geom files. You can add them here: +#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/sbasis-math.h> +#include <2geom/piecewise.h> +#include <2geom/crossing.h> +#include <2geom/path-intersection.h> + +namespace Inkscape { +namespace LivePathEffect { + +LPEKnot::LPEKnot(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + // initialise your parameters here: + interruption_width(_("Interruption width"), _("Howmuch the lower strand is obscured by the upper."), "interruption_width", &wr, this, 1.2) +{ + // register all your parameters here, so Inkscape knows which parameters this effect has: + registerParameter( dynamic_cast(&interruption_width) ); +} + +LPEKnot::~LPEKnot() +{ + +} + + +/* ######################## + * Choose to implement one of the doEffect functions. You can delete or comment out the others. +*/ + +/* +void +LPEKnot::doEffect (SPCurve * curve) +{ + // spice this up to make the effect actually *do* something! +} + +NArtBpath * +LPEKnot::doEffect_nartbpath (NArtBpath * path_in) +{ + NArtBpath *path_out; + unsigned ret = 0; + while ( path_in[ret].code != NR_END ) { + ++ret; + } + unsigned len = ++ret; + path_out = g_new(NArtBpath, len); + + memcpy(path_out, path_in, len * sizeof(NArtBpath)); // spice this up to make the effect actually *do* something! + + return path_out; +} +*/ + +std::vector complementOf(Geom::Interval I, std::vector domain){ + std::vector ret; + double min = domain.front().min(); + double max = domain.back().max(); + Geom::Interval I1 = Geom::Interval(min,I.min()); + Geom::Interval I2 = Geom::Interval(I.max(),max); + + for (unsigned i = 0; i I1i = intersect(domain.at(i),I1); + if (I1i) ret.push_back(I1i.get()); + boost::optional I2i = intersect(domain.at(i),I2); + if (I2i) ret.push_back(I2i.get()); + } + return ret; +} + +Geom::Interval +findShadowedTime(Geom::Path &patha, + Geom::Path &pathb, + Geom::Crossing crossing, + unsigned idx, double width){ + using namespace Geom; + double curveidx, timeoncurve = modf(crossing.getOtherTime(idx),&curveidx); + if(curveidx == pathb.size() && timeoncurve == 0) { curveidx--; timeoncurve = 0.99999;} + assert(curveidx >= 0 && curveidx < pathb.size()); + + std::vector MV = pathb[unsigned(curveidx)].pointAndDerivatives(timeoncurve,2); + Point T = unit_vector(MV.at(1)); + Point N = T.cw(); + Point A = MV.at(0)-10*width*T, B = MV.at(0)+10*width*T; + + std::vector cutter; + Geom::Path cutterLeft = Geom::Path(); + Geom::Path cutterRight = Geom::Path(); + cutterLeft.append (LineSegment (A-width*N, B-width*N)); + cutterRight.append(LineSegment (A+width*N, B+width*N)); + cutter.push_back(cutterLeft); + cutter.push_back(cutterRight); + + std::vector patha_as_vect = std::vector(1,patha); + + CrossingSet crossingTable = crossings (patha_as_vect, cutter); + double t0 = crossing.getTime(idx); + double tmin = 0,tmax = patha.size()-0.0001; + assert(crossingTable.size()>=1); + for (unsigned c=0; ctmin and tt0) tmax = t; + } + //return Interval(t0-0.1,t0+0.1); + return Interval(tmin,tmax); +} + + +std::vector +LPEKnot::doEffect_path (std::vector & path_in) +{ + using namespace Geom; + std::vector path_out; + double width = interruption_width; + + CrossingSet crossingTable = crossings_among(path_in); + for (unsigned i = 0; i < crossingTable.size(); i++){ + std::vector dom; + dom.push_back(Interval(0.,path_in.at(i).size()-0.00001)); + //TODO: handle closed curves... + for (unsigned crs = 0; crs < crossingTable.at(i).size(); crs++){ + Crossing crossing = crossingTable.at(i).at(crs); + unsigned j = crossing.getOther(i); + //TODO: select dir according to a parameter... + if ((crossing.dir and crossing.a==i) or (not crossing.dir and crossing.b==i) or (i==j)){ + if (i==j and not crossing.dir) { + double temp = crossing.ta; + crossing.ta = crossing.tb; + crossing.tb = temp; + crossing.dir = not crossing.dir; + } + Interval hidden = findShadowedTime(path_in.at(i),path_in.at(j),crossing,i,width); + dom = complementOf(hidden,dom); + } + } + for (unsigned comp = 0; comp < dom.size(); comp++){ + assert(dom.at(comp).min() >=0 and dom.at(comp).max() < path_in.at(i).size()); + path_out.push_back(path_in.at(i).portion(dom.at(comp))); + } + +// std::vector MV = path_in[0][0].pointAndDerivatives(crossingTable[0][0].getTime(0),2); +// Point U = unit_vector(MV[1]); +// Point N = U.cw(); +// Point A = MV[0]-10.*width*U, B = MV[0]+10*width*U; + +// Geom::Path cutter; +// cutter = Geom::Path(); +// cutter.append( LineSegment(A+width*N,B+width*N)); +// path_out.push_back(cutter); +// cutter = Geom::Path(); +// cutter.append( LineSegment(A-width*N,B-width*N)); +// path_out.push_back(cutter); + + } + return path_out; +} + + +/* +Geom::Piecewise > +addLinearEnds (Geom::Piecewise > & m){ + using namespace Geom; + Piecewise > output; + Piecewise > start; + Piecewise > end; + double x,y,vx,vy; + + x = m.segs.front()[0].at0(); + y = m.segs.front()[1].at0(); + vx = m.segs.front()[0][1][0]+Tri(m.segs.front()[0][0]); + vy = m.segs.front()[1][1][0]+Tri(m.segs.front()[1][0]); + start = Piecewise >(D2(Linear (x-vx,x),Linear (y-vy,y))); + start.offsetDomain(m.cuts.front()-1.); + + x = m.segs.back()[0].at1(); + y = m.segs.back()[1].at1(); + vx = -m.segs.back()[0][1][1]+Tri(m.segs.back()[0][0]);; + vy = -m.segs.back()[1][1][1]+Tri(m.segs.back()[1][0]);; + end = Piecewise >(D2(Linear (x,x+vx),Linear (y,y+vy))); + //end.offsetDomain(m.cuts.back()); + + output = start; + output.concat(m); + output.concat(end); + return output; +} + +Geom::Piecewise > +LPEKnot::doEffect_pwd2 (Geom::Piecewise > & pwd2_in) +{ + using namespace Geom; + + + Piecewise > output; + Piecewise > m = addLinearEnds(pwd2_in); + + Piecewise > v = derivative(pwd2_in); + Piecewise > n = unitVector(v); + +// // -------- Pleins et delies vs courbure ou direction... +// Piecewise > a = derivative(v); +// Piecewise a_cross_n = cross(a,n); +// Piecewise v_dot_n = dot(v,n); +// //Piecewise > rfrac = sectionize(D2 >(a_cross_n,v_dot_n)); +// //Piecewise h = atan2(rfrac)*interruption_width; +// Piecewise h = reciprocal(curvature(pwd2_in))*interruption_width; +// +// // Piecewise > dir = Piecewise >(D2(Linear(0),Linear(-1))); +// // Piecewise h = dot(n,dir)+1.; +// // h *= h*(interruption_width/4.); +// +// n = rot90(n); +// output = pwd2_in+h*n; +// output.concat(pwd2_in-h*n); +// +// //----------- + + //output.concat(m); + return output; +} +*/ + +/* ######################## */ + +} //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-knot.h b/src/live_effects/lpe-knot.h new file mode 100644 index 000000000..589413285 --- /dev/null +++ b/src/live_effects/lpe-knot.h @@ -0,0 +1,47 @@ +#ifndef INKSCAPE_LPE_KNOT_H +#define INKSCAPE_LPE_KNOT_H + +/** \file + * LPE implementation, see lpe-knot.cpp. + */ + +/* + * Authors: + * JFB, but derived from Johan Engelen! +* +* Copyright (C) Johan Engelen 2007 + * + * 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/point.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPEKnot : public Effect { +public: + LPEKnot(LivePathEffectObject *lpeobject); + virtual ~LPEKnot(); + +// Choose to implement one of the doEffect functions. You can delete or comment out the others. +// virtual void doEffect (SPCurve * curve); +// virtual NArtBpath * doEffect_nartbpath (NArtBpath * path_in); + virtual std::vector doEffect_path (std::vector & path_in); +// virtual Geom::Piecewise > doEffect_pwd2 (Geom::Piecewise > & pwd2_in); + +private: + // add the parameters for your effect here: + ScalarParam interruption_width; + // there are all kinds of parameters. Check the /live_effects/parameter directory which types exist! + + LPEKnot(const LPEKnot&); + LPEKnot& operator=(const LPEKnot&); +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif diff --git a/src/live_effects/lpe-sketch.cpp b/src/live_effects/lpe-sketch.cpp new file mode 100644 index 000000000..9b49122fe --- /dev/null +++ b/src/live_effects/lpe-sketch.cpp @@ -0,0 +1,347 @@ +#define INKSCAPE_LPE_SKETCH_CPP +/** \file + * LPE implementation + */ +/* + * Authors: + * Johan Engelen +* +* Copyright (C) Johan Engelen 2007 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-sketch.h" +#include "display/curve.h" +#include + +// You might need to include other 2geom files. You can add them here: +#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/sbasis-math.h> +#include <2geom/piecewise.h> +#include <2geom/crossing.h> +#include <2geom/path-intersection.h> + +namespace Inkscape { +namespace LivePathEffect { + +LPESketch::LPESketch(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + // initialise your parameters here: + //testpointA(_("Test Point A"), _("Test A"), "ptA", &wr, this, Geom::Point(100,100)), + nbiter_approxstrokes(_("Nb of iterations"), _("Draw that many approximating strokes sequences."), "nbiter_approxstrokes", &wr, this, 5), + strokelength(_("Max stroke length"), _("Maximal length of approximated strokes."), "strokelength", &wr, this, 100.), + strokelength_rdm(_("Randomness"), _("Random variation of stroke length (relative to max. length)."), "strokelength_rdm", &wr, this, .3), + strokeoverlap(_("Max. overlap"), _("How much successive strokes should overlap (relative to max. length)."), "strokeoverlap", &wr, this, .3), + strokeoverlap_rdm(_("Randomness"), _("Random variation of overlap (relative to max. overlap)"), "strokeoverlap_rdm", &wr, this, .3), + ends_tolerance(_("Max. ends tolerance"), _("Max. distance between original and approximated paths ends (relative to max. length)."), "ends_tolerance", &wr, this, .1), + parallel_offset(_("Average offset"), _("Average distance to original stroke(try 0.)."), "parallel_offset", &wr, this, 5.), + tremble_size(_("Max. tremble"), _("Maximal tremble magnitude."), "tremble_size", &wr, this, 5.), + tremble_frequency(_("Tremble frequency"), _("Typical nb of tremble 'period' in a stroke."), "tremble_frequency", &wr, this, 1.), + nbtangents(_("Nb of construction Lines"), _("How many construction lines (tangents) to draw?"), "nbtangents", &wr, this, 5), + tgtscale(_("Scale"), _("Scale factor relating curvature and length of construction lines(try 5*avarage offset) )"), "tgtscale", &wr, this, 10.0), + tgtlength(_("Max. length"), _("Max. length of construction lines."), "tgtlength", &wr, this, 100.0), + tgtlength_rdm(_("Randomness"), _("Random variation of construction lines length."), "tgtlength_rdm", &wr, this, .3) +{ + // register all your parameters here, so Inkscape knows which parameters this effect has: + //Add some comment in the UI: *warning* the precise output of this effect might change in future releases! + //convert to path if you want to keep exact output unchanged in future releases... + //registerParameter( dynamic_cast(&testpointA) ); + registerParameter( dynamic_cast(&nbiter_approxstrokes) ); + registerParameter( dynamic_cast(&strokelength) ); + registerParameter( dynamic_cast(&strokelength_rdm) ); + registerParameter( dynamic_cast(&strokeoverlap) ); + registerParameter( dynamic_cast(&strokeoverlap_rdm) ); + registerParameter( dynamic_cast(&ends_tolerance) ); + registerParameter( dynamic_cast(¶llel_offset) ); + registerParameter( dynamic_cast(&tremble_size) ); + registerParameter( dynamic_cast(&tremble_frequency) ); + registerParameter( dynamic_cast(&nbtangents) ); + registerParameter( dynamic_cast(&tgtscale) ); + registerParameter( dynamic_cast(&tgtlength) ); + registerParameter( dynamic_cast(&tgtlength_rdm) ); + + + nbiter_approxstrokes.param_make_integer(); + nbiter_approxstrokes.param_set_range(0, NR_HUGE); + strokelength.param_set_range(1, NR_HUGE); + strokelength.param_set_increments(1., 5.); + strokelength_rdm.param_set_range(0, 1.); + strokeoverlap.param_set_range(0, 1.); + strokeoverlap.param_set_increments(0.1, 0.30); + ends_tolerance.param_set_range(0., 1.); + parallel_offset.param_set_range(0, NR_HUGE); + tremble_frequency.param_set_range(0.01, 100.); + tremble_frequency.param_set_increments(.5, 1.5); + strokeoverlap_rdm.param_set_range(0, 1.); + + nbtangents.param_make_integer(); + nbtangents.param_set_range(0, NR_HUGE); + tgtscale.param_set_range(0, NR_HUGE); + tgtscale.param_set_increments(.1, .5); + tgtlength.param_set_range(0, NR_HUGE); + tgtlength.param_set_increments(1., 5.); + tgtlength_rdm.param_set_range(0, 1.); +} + +LPESketch::~LPESketch() +{ + +} + +/* +Geom::Piecewise > +addLinearEnds (Geom::Piecewise > & m){ + using namespace Geom; + Piecewise > output; + Piecewise > start; + Piecewise > end; + double x,y,vx,vy; + + x = m.segs.front()[0].at0(); + y = m.segs.front()[1].at0(); + vx = m.segs.front()[0][1][0]+Tri(m.segs.front()[0][0]); + vy = m.segs.front()[1][1][0]+Tri(m.segs.front()[1][0]); + start = Piecewise >(D2(Linear (x-vx,x),Linear (y-vy,y))); + start.offsetDomain(m.cuts.front()-1.); + + x = m.segs.back()[0].at1(); + y = m.segs.back()[1].at1(); + vx = -m.segs.back()[0][1][1]+Tri(m.segs.back()[0][0]);; + vy = -m.segs.back()[1][1][1]+Tri(m.segs.back()[1][0]);; + end = Piecewise >(D2(Linear (x,x+vx),Linear (y,y+vy))); + //end.offsetDomain(m.cuts.back()); + + output = start; + output.concat(m); + output.concat(end); + return output; +} +*/ + +//TODO: does this already exist in 2Geom? if not, move this there... +std::vector > > +split_at_discontinuities (Geom::Piecewise > & pwsbin, double tol = .0001) +{ + using namespace Geom; + std::vector > > ret; + unsigned piece_start = 0; + for (unsigned i=0; i tol){ + Piecewise > piece; + piece.cuts.push_back(pwsbin.cuts[piece_start]); + for (unsigned j = piece_start; js1]... +Geom::Piecewise > +LPESketch::computePerturbation (double s0, double s1){ + using namespace Geom; + Piecewise >res; + + //global offset for this stroke. + double offsetX = parallel_offset-parallel_offset.get_value(); + double offsetY = parallel_offset-parallel_offset.get_value(); + Point A,dA,B,dB,offset = Point(offsetX,offsetY); + //start point A + for (unsigned dim=0; dim<2; dim++){ + A[dim] = offset[dim] + 2*tremble_size-tremble_size.get_value(); + dA[dim] = 2*tremble_size-tremble_size.get_value(); + } + //compute howmany deg 3 sbasis to concat according to frequency. + unsigned count = unsigned((s1-s0)/strokelength*tremble_frequency)+1; + for (unsigned i=0; i perturb = D2(); + for (unsigned dim=0; dim<2; dim++){ + B[dim] = offset[dim] + 2*tremble_size-tremble_size.get_value(); + perturb[dim].push_back(Linear(A[dim],B[dim])); + dA[dim] = dA[dim]-B[dim]+A[dim]; + dB[dim] = 2*tremble_size-tremble_size.get_value(); + perturb[dim].push_back(Linear(dA[dim],dB[dim])); + } + A = B; + dA = B-A-dB; + res.concat(Piecewise >(perturb)); + } + res.setDomain(Interval(s0,s0+count*strokelength/tremble_frequency)); + return res; +} + + +// Main effect body... +Geom::Piecewise > +LPESketch::doEffect_pwd2 (Geom::Piecewise > & pwd2_in) +{ + using namespace Geom; + Piecewise > output; + + + //init random parameters. + parallel_offset.resetRandomizer(); + strokelength_rdm.resetRandomizer(); + strokeoverlap_rdm.resetRandomizer(); + tgtlength_rdm.resetRandomizer(); + + // some variables for futur use. + // notations will be : t = path time, s = distance from start along the path. + Piecewise pathlength; + std::vector times; + double total_length; + + //TODO: split Construction Lines/Approximated Strokes into two separate effects? + + //----- Approximated Strokes. + //TODO: choose length & offset according to curvature? + + //TODO: retrieve components from path, dont merge to separate afterward! + know if closed! + std::vector > > pieces_in = split_at_discontinuities (pwd2_in); + + //work separately on each component. + for (unsigned pieceidx = 0; pieceidx < pieces_in.size(); pieceidx++){ + + Piecewise > piece = pieces_in[pieceidx]; + Piecewise piecelength = arcLengthSb(piece,.1); + pathlength.concat(piecelength); + + total_length = piecelength.segs.back().at1()-piecelength.segs.front().at0(); + + //TODO: better check this on the Geom::Path. + bool closed = piece.segs.front().at0() == piece.segs.back().at1(); + if (closed){ + piece.concat(piece); + piecelength.concat(piecelength+total_length); + } + + for (unsigned i = 0; i=0);//this should not happen!! + if (!closed && s1>total_length - ends_tolerance.get_value()*strokelength) break; + if ( closed && s0>total_length + s0_initial) break; + + times = roots(piecelength-s0); + if (times.size()==0) break;//we should not be there. + t0 = times[0]; + + // pick a new end point (s1 = s0 + strokelength). + s1 = s0 + strokelength*(1-strokelength_rdm); + // don't let it go beyond the end of the orgiginal path. + // TODO/FIXME: this might result in short strokes near the end... + if (!closed && s1>total_length-ends_tolerance.get_value()*strokelength){ + done = true; + //!!the root solver might miss s1==total_length... + if (s1>total_length){s1 = total_length - ends_tolerance*strokelength-0.0001;} + } + if (closed && s1>total_length + s0_initial){ + done = true; + if (closed && s1>2*total_length){s1 = 2*total_length - strokeoverlap*(1-strokeoverlap_rdm)*strokelength-0.0001;} + } + times = roots(piecelength-s1); + if (times.size()==0) break;//we should not be there. + t1 = times[0]; + + //pick a rdm perturbation, and collect the perturbed piece into output. + Piecewise > pwperturb = computePerturbation(s0,s1); + pwperturb = compose(pwperturb,portion(piecelength,t0,t1)); + output.concat(portion(piece,t0,t1)+pwperturb); + + //step points: s0 = s1 - overlap. + //TODO: make sure this has to end? + s0 = s1 - strokeoverlap*(1-strokeoverlap_rdm)*strokelength; + } + } + } + + + //----- Construction lines. + //TODO: choose places according to curvature?. + + //at this point we have: + //pathlength = arcLengthSb(pwd2_in,.1); + total_length = pathlength.segs.back().at1(); + Piecewise > m = pwd2_in; + Piecewise > v = derivative(pwd2_in); + Piecewise > a = derivative(v); + for (unsigned i=0; i0);//there should be one and only one solution! + double t = times[0]; + Point m_t = m(t), v_t = v(t), a_t = a(t); + //Compute tgt length according to curvature (not exceeding tgtlength) so that + // dist to origninal curve ~ 4 * (parallel_offset+tremble_size). + //TODO: put this 4 as a parameter in the UI... + //TODO: what if with v=0? + double l = tgtlength*(1-tgtlength_rdm)/v_t.length(); + double r = pow(v_t.length(),3)/cross(a_t,v_t); + r = sqrt((2*fabs(r)-tgtscale)*tgtscale)/v_t.length(); + l=(r tgt = D2(); + for (unsigned dim=0; dim<2; dim++){ + tgt[dim] = SBasis(Linear(m_t[dim]-v_t[dim]*l, m_t[dim]+v_t[dim]*l)); + } + output.concat(Piecewise >(tgt)); + } + +// // -------- Pleins et delies vs courbure ou direction... +// Piecewise > a = derivative(v); +// Piecewise a_cross_n = cross(a,n); +// Piecewise v_dot_n = dot(v,n); +// //Piecewise > rfrac = sectionize(D2 >(a_cross_n,v_dot_n)); +// //Piecewise h = atan2(rfrac)*para1; +// Piecewise h = reciprocal(curvature(piece))*para1; +// +// // Piecewise > dir = Piecewise >(D2(Linear(0),Linear(-1))); +// // Piecewise h = dot(n,dir)+1.; +// // h *= h*(para1/4.); +// +// n = rot90(n); +// output = piece+h*n; +// output.concat(piece-h*n); +// +// //----------- + + //output.concat(m); + return output; +} + +/* ######################## */ + +} //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-sketch.h b/src/live_effects/lpe-sketch.h new file mode 100644 index 000000000..fb6bdda3a --- /dev/null +++ b/src/live_effects/lpe-sketch.h @@ -0,0 +1,63 @@ +#ifndef INKSCAPE_LPE_SKETCH_H +#define INKSCAPE_LPE_SKETCH_H + +/** \file + * LPE implementation, see lpe-sketch.cpp. + */ + +/* + * Authors: + * JFB, but derived from Johan Engelen! +* +* Copyright (C) Johan Engelen 2007 + * + * 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/random.h" +#include "live_effects/parameter/point.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPESketch : public Effect { +public: + LPESketch(LivePathEffectObject *lpeobject); + virtual ~LPESketch(); + +// Choose to implement one of the doEffect functions. You can delete or comment out the others. +// virtual void doEffect (SPCurve * curve); +// virtual NArtBpath * doEffect_nartbpath (NArtBpath * path_in); +// virtual std::vector doEffect_path (std::vector & path_in); + virtual Geom::Piecewise > doEffect_pwd2 (Geom::Piecewise > & pwd2_in); + +private: + // add the parameters for your effect here: + //PointParam testpointA; + ScalarParam nbiter_approxstrokes; + ScalarParam strokelength; + RandomParam strokelength_rdm; + ScalarParam strokeoverlap; + RandomParam strokeoverlap_rdm; + RandomParam ends_tolerance; + RandomParam parallel_offset; + RandomParam tremble_size; + ScalarParam tremble_frequency; + ScalarParam nbtangents; + ScalarParam tgtscale; + ScalarParam tgtlength; + RandomParam tgtlength_rdm; + + LPESketch(const LPESketch&); + LPESketch& operator=(const LPESketch&); + + Geom::Piecewise > computePerturbation (double s0, double s1); + +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp new file mode 100644 index 000000000..fa05cb5ff --- /dev/null +++ b/src/live_effects/lpe-vonkoch.cpp @@ -0,0 +1,158 @@ +#define INKSCAPE_LPE_VONKOCH_CPP + +/* + * Copyright (C) Johan Engelen 2007 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-vonkoch.h" +#include "sp-shape.h" +#include "sp-item.h" +#include "sp-path.h" +#include "display/curve.h" +#include +#include +#include "live_effects/n-art-bpath-2geom.h" +#include "svg/svg.h" +#include "ui/widget/scalar.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/piecewise.h> + +#include +using std::vector; + + +namespace Inkscape { +namespace LivePathEffect { + +LPEVonKoch::LPEVonKoch(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + generator(_("Generating path"), _("Path whos segments define the fractal"), "generator", &wr, this, "M0,0 L3,0 M0,1 L1,1 M 2,1 L3,1"), + nbgenerations(_("Nb of generations"), _("Depth of the recursion --- keep low!!"), "nbgenerations", &wr, this, 1), + drawall(_("Draw all generations"), _("If unchecked, draw only the last generation"), "drawall", &wr, this, false), + vertical_pattern(_("Original path is vertical"), _("Rotates the original 90 degrees, before generating the fractal"), "vertical", &wr, this, false) +{ + registerParameter( dynamic_cast(&generator) ); + registerParameter( dynamic_cast(&nbgenerations) ); + registerParameter( dynamic_cast(&drawall) ); + registerParameter( dynamic_cast(&vertical_pattern) ); + + nbgenerations.param_make_integer(); + nbgenerations.param_set_range(0, NR_HUGE); +} + +LPEVonKoch::~LPEVonKoch() +{ + +} + + +std::vector +LPEVonKoch::doEffect_path (std::vector & path_in) +{ + using namespace Geom; + std::vector generating_path = path_from_piecewise(generator,.01);//TODO what should that tolerance be? + Point p = generating_path[0].pointAt(0.001); + Point u = generating_path[0].pointAt(0.999)-generating_path[0].pointAt(0.001); + Matrix m0 = Matrix(u[X], u[Y],-u[Y], u[X], p[X], p[Y]); + m0 = m0.inverse(); + + std::vector transforms; + for (unsigned i=0; i0); + + std::vector transforms; + transforms.push_back(Matrix(.5, 0., 0., .5, 0.,50.)); + transforms.push_back(Matrix(.5, 0., 0., .5, 100., 0.)); +*/ + + std::vector pathi = path_in; + std::vector path_out = path_in; + + for (unsigned i = 0; i(); + for (unsigned j = 0; j > pwd2; + std::vector temppath = SVGD_to_2GeomPath( SP_OBJECT_REPR(item)->attribute("inkscape:original-d")); + for (unsigned int i=0; i < temppath.size(); i++) { + pwd2.concat( temppath[i].toPwSb() ); + } + + D2 > d2pw = make_cuts_independant(pwd2); + Interval bndsX = bounds_exact(d2pw[0]); + Interval bndsY = bounds_exact(d2pw[1]); + Point start(bndsX.min(), (bndsY.max()+bndsY.min())/2); + Point end(bndsX.max(), (bndsY.max()+bndsY.min())/2); + + std::vector paths; + Geom::Path path; + path = Geom::Path(); + path.start( start ); + path.appendNew( end ); + paths.push_back(path); + paths.push_back(path * Matrix(1./3,0,0,1./3,start[X]*2./3,start[Y]*2./3 + bndsY.extent()/2)); + paths.push_back(path * Matrix(1./3,0,0,1./3, end[X]*2./3, end[Y]*2./3 + bndsY.extent()/2)); + + //generator.param_set_and_write_new_value( path.toPwSb() ); + generator.param_set_and_write_new_value( paths_to_pw(paths) ); + + +// Piecewise > default_gen; +// default_gen.concat(Piecewise >(D2(Linear(bndsX.min(),bndsX.max()),Linear((bndsY.min()+bndsY.max())/2)))); +// default_gen.concat(Piecewise >(D2(Linear(bndsX.max(),bndsX.max()+bndsX.extent()/2),Linear((bndsY.min()+bndsY.max())/2)))); +// generator.param_set_and_write_new_value( default_gen ); +} + +void +LPEVonKoch::transform_multiply(Geom::Matrix const& postmul, bool set) +{ + // TODO: implement correct transformation instead of this default behavior + Effect::transform_multiply(postmul, set); +} + + +} // namespace LivePathEffect +} /* 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-vonkoch.h b/src/live_effects/lpe-vonkoch.h new file mode 100644 index 000000000..f32242d74 --- /dev/null +++ b/src/live_effects/lpe-vonkoch.h @@ -0,0 +1,46 @@ +#ifndef INKSCAPE_LPE_VONKOCH_H +#define INKSCAPE_LPE_VONKOCH_H + +/* + * Inkscape::LPEVonKoch + * +* Copyright (C) Johan Engelen 2007 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/effect.h" +#include "live_effects/parameter/path.h" +#include "live_effects/parameter/enum.h" +#include "live_effects/parameter/bool.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPEVonKoch : public Effect { +public: + LPEVonKoch(LivePathEffectObject *lpeobject); + virtual ~LPEVonKoch(); + + virtual std::vector doEffect_path (std::vector & path_in); + + virtual void resetDefaults(SPItem * item); + + virtual void transform_multiply(Geom::Matrix const& postmul, bool set); + +private: + ScalarParam nbgenerations; + PathParam generator; + BoolParam drawall; + BoolParam vertical_pattern; + + void on_pattern_pasted(); + + LPEVonKoch(const LPEVonKoch&); + LPEVonKoch& operator=(const LPEVonKoch&); +}; + +}; //namespace LivePathEffect +}; //namespace Inkscape + +#endif -- 2.39.5