From 500ef69516ef3c5dd1517f5d09c75ebe37453882 Mon Sep 17 00:00:00 2001 From: jfbarraud Date: Sun, 16 Mar 2008 13:36:04 +0000 Subject: [PATCH] lpe-vonkoch: complexity bound + choose bbox/segment as reference. lpe-knot: support for self intersecting bezier (will be moved to 2geom). --- src/live_effects/lpe-knot.cpp | 111 ++++++++++++++++--------------- src/live_effects/lpe-vonkoch.cpp | 98 +++++++++++++++++++++------ src/live_effects/lpe-vonkoch.h | 39 +++++++++-- 3 files changed, 171 insertions(+), 77 deletions(-) diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp index a82da903b..bd9c0ca68 100644 --- a/src/live_effects/lpe-knot.cpp +++ b/src/live_effects/lpe-knot.cpp @@ -4,9 +4,9 @@ */ /* * Authors: - * Johan Engelen + * JF Barraud * -* Copyright (C) Johan Engelen 2007 +* Copyright (C) JF Barraud 2007 * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -15,15 +15,14 @@ #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/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/sbasis-math.h> +//#include <2geom/piecewise.h> #include <2geom/crossing.h> #include <2geom/path-intersection.h> @@ -44,35 +43,7 @@ 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; -} -*/ - +//remove an interval from an union of intervals. std::vector complementOf(Geom::Interval I, std::vector domain){ std::vector ret; double min = domain.front().min(); @@ -128,6 +99,54 @@ findShadowedTime(Geom::Path &patha, return Interval(tmin,tmax); } +//Just a try; this should be moved to 2geom if ever it works. +std::vector +split_loopy_bezier (std::vector & path_in){ + + std::vector ret; + std::vector::iterator pi=path_in.begin(); + for(; pi != path_in.end(); pi++) { + ret.push_back(Geom::Path()); + for (Geom::Path::iterator curve(pi->begin()),end(pi->end()); curve != end; ++curve){ + + //is the current curve a cubic bezier? + if(Geom::CubicBezier const *cubic_bezier = dynamic_cast(&(*curve))){ + Geom::CubicBezier theCurve = *cubic_bezier; + std::vector A = theCurve.points(); + + //is there a crossing in the polygon? + if( cross(A[2]-A[0],A[1]-A[0])*cross(A[3]-A[0],A[1]-A[0])<0 && + cross(A[0]-A[3],A[2]-A[3])*cross(A[1]-A[3],A[2]-A[3])<0){ + + //split the curve where the tangent is parallel to the chord through end points. + double a = cross(A[3]-A[0],A[1]-A[2]); + double c = cross(A[3]-A[0],A[1]-A[0]); + double t; //where to split; solution of 3*at^2-(a+c)t +c = 0. + //TODO: don't we have a clean deg 2 equation solver?... + if (fabs(a)<.0001){ + t = .5; + }else{ + double delta = a*a-a*c+c*c; + t = ((a+c)-sqrt(delta))/2/a; + if ( t<0 || t>1 ) {t = ((a+c)+sqrt(delta))/2/a;} + } + //TODO: shouldn't Path have subdivide method? + std::pair, Geom::BezierCurve<3> > splitCurve; + splitCurve = theCurve.subdivide(t); + ret.back().append(splitCurve.first); + ret.back().append(splitCurve.second); + + }else{//cubic bezier but no crossing. + ret.back().append(*curve); + } + }else{//not cubic bezier. + ret.back().append(*curve); + } + } + } + return ret; +} + std::vector LPEKnot::doEffect_path (std::vector & path_in) @@ -136,6 +155,8 @@ LPEKnot::doEffect_path (std::vector & path_in) std::vector path_out; double width = interruption_width; + path_in = split_loopy_bezier(path_in); + CrossingSet crossingTable = crossings_among(path_in); for (unsigned i = 0; i < crossingTable.size(); i++){ std::vector dom; @@ -160,20 +181,6 @@ LPEKnot::doEffect_path (std::vector & path_in) 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; } diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 82ba1bf2d..cb5ea126c 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -1,7 +1,7 @@ #define INKSCAPE_LPE_VONKOCH_CPP /* - * Copyright (C) Johan Engelen 2007 + * Copyright (C) JF Barraud 2007 * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -33,20 +33,41 @@ using std::vector; namespace Inkscape { namespace LivePathEffect { +VonKochPathParam::~VonKochPathParam() +{ +} + +void +VonKochPathParam::param_setup_nodepath(Inkscape::NodePath::Path *np) +{ + PathParam::param_setup_nodepath(np); + np->straight_path = true; +} + +static const Util::EnumData VonKochRefTypeData[VKREF_END] = { + {VKREF_BBOX, N_("Bounding box"), "bbox"}, + {VKREF_SEG, N_("Last gen. segment"), "lastseg"}, +}; +static const Util::EnumDataConverter VonKochRefTypeConverter(VonKochRefTypeData, VKREF_END); + LPEVonKoch::LPEVonKoch(LivePathEffectObject *lpeobject) : Effect(lpeobject), nbgenerations(_("Nb of generations"), _("Depth of the recursion --- keep low!!"), "nbgenerations", &wr, this, 1), 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"), - 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) + drawall(_("Draw all generations"), _("If unchecked, draw only the last generation"), "drawall", &wr, this, true), + reftype(_("Reference"), _("Generating path segments define transforms in reference to bbox or last segment"), "reftype", VonKochRefTypeConverter, &wr, this, VKREF_BBOX), + maxComplexity(_("Max complexity"), _("Disable effect if the output is too complex"), "maxComplexity", &wr, this, 1000) { registerParameter( dynamic_cast(&generator) ); registerParameter( dynamic_cast(&nbgenerations) ); registerParameter( dynamic_cast(&drawall) ); - registerParameter( dynamic_cast(&vertical_pattern) ); + registerParameter( dynamic_cast(&reftype) ); + registerParameter( dynamic_cast(&maxComplexity) ); nbgenerations.param_make_integer(); nbgenerations.param_set_range(0, NR_HUGE); + maxComplexity.param_make_integer(); + maxComplexity.param_set_range(0, NR_HUGE); } LPEVonKoch::~LPEVonKoch() @@ -54,22 +75,35 @@ 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? - Rect bbox = path_in[0].boundsExact(); - for(unsigned i=1; i < path_in.size(); i++){ - bbox.unionWith(path_in[i].boundsExact()); + + //Collect transform matrices. + //FIXME: fusing/cutting nodes mix up component order in the path. This is why the last segment is used. + Matrix m0; + VonKochRefType type = reftype.get_value(); + if (type==VKREF_BBOX){ + Rect bbox = path_in[0].boundsExact(); + for(unsigned i=1; i < path_in.size(); i++){ + bbox.unionWith(path_in[i].boundsExact()); + } + m0 = Matrix(bbox[X].extent(),0,0,bbox[X].extent(), bbox.min()[X], (bbox.min()[Y]+bbox.max()[Y])/2); + }else{ + if (generating_path.size()==0) return path_in; + Point p = generating_path.back().back().pointAt(0); + Point u = generating_path.back().back().pointAt(0.999)-p; + m0 = Matrix(u[X], u[Y],-u[Y], u[X], p[X], p[Y]); } - Matrix m0 = Matrix(bbox[X].extent(),0,0,bbox[X].extent(), bbox.min()[X], bbox.min()[Y]); m0 = m0.inverse(); std::vector transforms; for (unsigned i=0; i & path_in) transforms.push_back(m); } } -/* - assert(generating_path.size()>0); + if (transforms.size()==0) return path_in; + + //Do nothing if the output is too complex... + int path_in_complexity = 0; + for (unsigned k = 0; k < path_in.size(); k++){ + path_in_complexity+=path_in[k].size(); + } + double complexity = pow(transforms.size(),nbgenerations)*path_in_complexity; + if (drawall.get_value()){ + int k = transforms.size(); + if(k>1){ + complexity = (pow(k,nbgenerations+1)-1)/(k-1)*path_in_complexity; + }else{ + complexity = nbgenerations*k*path_in_complexity; + } + }else{ + complexity = pow(transforms.size(),nbgenerations)*path_in_complexity; + } + if (complexity > double(maxComplexity)){ + return path_in; + } - std::vector transforms; - transforms.push_back(Matrix(.5, 0., 0., .5, 0.,50.)); - transforms.push_back(Matrix(.5, 0., 0., .5, 100., 0.)); -*/ - + //Generate path: std::vector pathi = path_in; std::vector path_out = path_in; - + for (unsigned i = 0; i(); + if (drawall.get_value()){ + path_out = path_in; + complexity = path_in_complexity; + }else{ + path_out = std::vector(); + complexity = 0; + } for (unsigned j = 0; j( 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)); + paths.push_back(path); //generator.param_set_and_write_new_value( path.toPwSb() ); generator.param_set_and_write_new_value( paths_to_pw(paths) ); diff --git a/src/live_effects/lpe-vonkoch.h b/src/live_effects/lpe-vonkoch.h index f32242d74..46f118536 100644 --- a/src/live_effects/lpe-vonkoch.h +++ b/src/live_effects/lpe-vonkoch.h @@ -4,19 +4,49 @@ /* * Inkscape::LPEVonKoch * -* Copyright (C) Johan Engelen 2007 + * Copyright (C) JF Barraud 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/path.h" #include "live_effects/parameter/enum.h" #include "live_effects/parameter/bool.h" +#include "live_effects/n-art-bpath-2geom.h" + +// needed for on-canvas editting: +#include "tools-switch.h" +#include "shape-editor.h" +#include "node-context.h" +#include "desktop-handles.h" +#include "selection.h" +#include "nodepath.h" + + namespace Inkscape { namespace LivePathEffect { +enum VonKochRefType { + VKREF_BBOX = 0, + VKREF_SEG, + VKREF_END // This must be last +}; + +class VonKochPathParam : public PathParam{ +public: + VonKochPathParam ( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Inkscape::UI::Widget::Registry* wr, + Effect* effect, + const gchar * default_value = "M0,0 L1,1"):PathParam(label,tip,key,wr,effect,default_value){}; + virtual ~VonKochPathParam(); + virtual void param_setup_nodepath(Inkscape::NodePath::Path *np); + }; + class LPEVonKoch : public Effect { public: LPEVonKoch(LivePathEffectObject *lpeobject); @@ -30,11 +60,12 @@ public: private: ScalarParam nbgenerations; - PathParam generator; + VonKochPathParam generator; BoolParam drawall; - BoolParam vertical_pattern; + EnumParam reftype; + ScalarParam maxComplexity; - void on_pattern_pasted(); + //void on_pattern_pasted(); LPEVonKoch(const LPEVonKoch&); LPEVonKoch& operator=(const LPEVonKoch&); -- 2.30.2