diff options
| author | Ted Gould <ted@gould.cx> | 2008-10-11 15:16:23 +0000 |
|---|---|---|
| committer | Ted Gould <ted@canonical.com> | 2008-10-11 15:16:23 +0000 |
| commit | 2f5eb047d9e05be5e68549ef6b75070d2faa7d2f (patch) | |
| tree | ca2e94164b6d7aaebfc17196ca46bfc825a7665a /src/live_effects | |
| parent | Merge from trunk. (diff) | |
| download | inkscape-2f5eb047d9e05be5e68549ef6b75070d2faa7d2f.tar.gz inkscape-2f5eb047d9e05be5e68549ef6b75070d2faa7d2f.zip | |
Merging from trunk
(bzr r6884)
Diffstat (limited to 'src/live_effects')
| -rw-r--r-- | src/live_effects/lpe-knot.cpp | 64 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.cpp | 2 | ||||
| -rw-r--r-- | src/live_effects/lpe-ruler.cpp | 66 | ||||
| -rw-r--r-- | src/live_effects/lpe-sketch.cpp | 4 | ||||
| -rw-r--r-- | src/live_effects/lpe-spiro.cpp | 65 | ||||
| -rw-r--r-- | src/live_effects/lpe-vonkoch.cpp | 233 | ||||
| -rw-r--r-- | src/live_effects/lpe-vonkoch.h | 48 | ||||
| -rw-r--r-- | src/live_effects/lpegroupbbox.cpp | 2 | ||||
| -rw-r--r-- | src/live_effects/lpeobject.cpp | 43 | ||||
| -rw-r--r-- | src/live_effects/lpeobject.h | 37 | ||||
| -rw-r--r-- | src/live_effects/parameter/pointparam-knotholder.cpp | 3 |
11 files changed, 399 insertions, 168 deletions
diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp index 418f43b3c..4504f4467 100644 --- a/src/live_effects/lpe-knot.cpp +++ b/src/live_effects/lpe-knot.cpp @@ -86,7 +86,8 @@ findShadowedTime(Geom::Path const &patha, cutterPath.appendNew<LineSegment> (B-width*N); cutterPath.appendNew<LineSegment> (B+width*N); cutterPath.appendNew<LineSegment> (A+width*N); - cutterPath.appendNew<LineSegment> (A-width*N); + cutterPath.close(); + //cutterPath.appendNew<LineSegment> (A-width*N); cutter.push_back(cutterPath); std::vector<Geom::Path> patha_as_vect = std::vector<Geom::Path>(1,patha); @@ -135,16 +136,47 @@ split_at_horiz_vert_tgt (std::vector<Geom::Path> const & path_in){ //LPEKnot specific Crossing Data manipulation. //--------------------------------------------------------------------------- -//TODO: evaluate how lpeknot specific that is. Worth being moved to 2geom? +//TODO: Fix this in 2Geom: I think CrossingSets should not contain duplicates. +Geom::CrossingSet crossingSet_remove_double(Geom::CrossingSet const &input){ + Geom::CrossingSet result(input.size()); + //Yeah, I know, there is a "unique" algorithm for that... + Geom::Crossing last; + for( unsigned i=0; i<input.size(); i++){ + for( unsigned j=0; j<input[i].size(); j++){ + if( j==0 || !(input[i][j]==last) ){ + result[i].push_back(input[i][j]); + last = input[i][j]; + }else{ + g_warning("Duplicate found in a Geom::CrossingSet!"); + } + } + } + return result; +} + +//TODO: evaluate how usefull/lpeknot specific that is. Worth being moved to 2geom? (I doubt it) namespace LPEKnotNS { +//Yet another crossing data representation. Not sure at all it is usefull! +// +: >Given a point, you immediately know which strings are meeting there, +// and the index of the crossing along each string. This makes it easy to check +// topology change. In a CrossingSet, you have to do some search to know the index +// of the crossing along "the second" string (i.e. find the symetric crossing)... +// >Each point is stored only once. +// However, we don't have so many crossing points in general, so none of these points might be relevant. +// +// -: one more clumsy data representation, and "parallelism" failures to expect... +// (in particular, duplicates are hateful with this respect...) + CrossingPoints::CrossingPoints(Geom::CrossingSet const &input, std::vector<Geom::Path> const &path) : std::vector<CrossingPoint>() { using namespace Geom; + g_print("JF>\nCrossing set content:\n"); for( unsigned i=0; i<input.size(); i++){ Crossings i_crossings = input[i]; for( unsigned n=0; n<i_crossings.size(); n++ ){ Crossing c = i_crossings[n]; + g_print("JF> (%u,%u) at times (%f,%f) ----->",c.a,c.b,c.ta,c.tb); unsigned j = c.getOther(i); if (i<j || (i==j && c.ta<c.tb) ){ CrossingPoint cp; @@ -163,6 +195,19 @@ CrossingPoints::CrossingPoints(Geom::CrossingSet const &input, std::vector<Geom: cp.nj = std::find(input[j].begin(),input[j].end(),c_bar)-input[j].begin(); cp.sign = 1; push_back(cp); + g_print("i=%u, ni=%u, j=%u, nj=%u\n",cp.i,cp.ni,cp.j,cp.nj); + } + else{ + g_print("\n"); + bool found = false; + for( unsigned ii=0; ii<input.size(); ii++){ + Crossings ii_crossings = input[ii]; + for( unsigned nn=0; nn<ii_crossings.size(); nn++ ){ + Crossing cc = ii_crossings[nn]; + if (cc.b==c.a && cc.a==c.b && cc.ta==c.tb && cc.tb==c.ta) found = true; + } + } + assert( found ); } } } @@ -214,6 +259,7 @@ CrossingPoints::get(unsigned const i, unsigned const ni) ((*this)[k].j==i && (*this)[k].nj==ni) ) return (*this)[k]; } + g_warning("LPEKnotNS::CrossingPoints::get error. %uth crossing along string %u not found.",ni,i); assert(false);//debug purpose... return CrossingPoint(); } @@ -335,6 +381,20 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &input_path) std::vector<Geom::Path> path_in = split_at_horiz_vert_tgt(input_path); CrossingSet crossingTable = crossings_among(path_in); + + for(unsigned i=0;i<crossingTable.size();i++){ + for(unsigned j=0;j<crossingTable[i].size();j++){ + g_print("JF>avant: %u,%u,%f,%f\n",crossingTable[i][j].a, crossingTable[i][j].b, crossingTable[i][j].ta, crossingTable[i][j].tb); + } + } + crossingTable = crossingSet_remove_double(crossingTable); + + for(unsigned i=0;i<crossingTable.size();i++){ + for(unsigned j=0;j<crossingTable[i].size();j++){ + g_print("JF>apres: %u,%u,%f,%f\n",crossingTable[i][j].a, crossingTable[i][j].b, crossingTable[i][j].ta, crossingTable[i][j].tb); + } + } + crossing_points = LPEKnotNS::CrossingPoints(crossingTable, path_in); crossing_points.inherit_signs(old_crdata); crossing_points_vector.param_set_and_write_new_value(crossing_points.to_vector()); diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index bb2b908de..cd724b70e 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -47,7 +47,7 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem *lpeitem) SPItem *item = SP_ITEM(lpeitem); Geom::Matrix t = sp_item_i2d_affine(item); - Geom::Rect bbox = to_2geom(*item->getBounds(t)); // fixme: what happens if getBounds does not return a valid rect? + Geom::Rect bbox = *item->getBounds(t); // fixme: what happens if getBounds does not return a valid rect? Point A(bbox.left(), bbox.bottom()); Point B(bbox.left(), bbox.top()); diff --git a/src/live_effects/lpe-ruler.cpp b/src/live_effects/lpe-ruler.cpp index 52ccc29e1..1df0e127a 100644 --- a/src/live_effects/lpe-ruler.cpp +++ b/src/live_effects/lpe-ruler.cpp @@ -19,6 +19,7 @@ #include "inkscape.h" #include "desktop.h" + namespace Inkscape { namespace LivePathEffect { @@ -110,34 +111,51 @@ LPERuler::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_i { using namespace Geom; - Piecewise<D2<SBasis> > pwd2_arclength = arc_length_parametrization(pwd2_in); - Point A(pwd2_arclength.firstValue()); - Point B(pwd2_arclength.lastValue()); - double path_length = Geom::length(pwd2_arclength); - - Piecewise<D2<SBasis> > n = -rot90(unitVector(derivative(pwd2_arclength))); - Piecewise<D2<SBasis> >output(pwd2_arclength); - - if (mark_dir == MARKDIR_RIGHT) { - n *= -1.0; - } - - int j = 0; const int mminterval = static_cast<int>(major_mark_steps); - const int j_shift = static_cast<int>(shift) % mminterval; - - /* draw the ruler */ - if ((border_marks == BORDERMARK_START || border_marks == BORDERMARK_BOTH) && (offset != 0.0 || j_shift != 0)) - output.concat (ruler_mark(A, n.firstValue(), MARK_MAJOR)); - for (double t = offset; t < path_length; t += mark_distance, ++j) { - if ((j % mminterval) == j_shift) { - output.concat (ruler_mark(pwd2_arclength(t), n(t), MARK_MAJOR)); + const int i_shift = static_cast<int>(shift) % mminterval; + int sign = (mark_dir == MARKDIR_RIGHT ? 1 : -1 ); + + Piecewise<D2<SBasis> >output(pwd2_in); + Piecewise<D2<SBasis> >speed = derivative(pwd2_in); + Piecewise<SBasis> arclength = arcLengthSb(pwd2_in); + double totlength = arclength.lastValue(); + + //find at which times to draw a mark: + std::vector<double> s_cuts; + for (double s = offset; s<totlength; s+=mark_distance){ + s_cuts.push_back(s); + } + std::vector<std::vector<double> > roots = multi_roots(arclength, s_cuts); + std::vector<double> t_cuts; + g_warning("times:"); + for (unsigned v=0; v<roots.size();v++){ + //FIXME: 2geom multi_roots solver seem to sometimes "repeat" solutions. + //Here, we are supposed to have one and only one solution for each s. + if(roots[v].size()>0) + t_cuts.push_back(roots[v][0]); + } + //draw the marks + for (unsigned i=0; i<t_cuts.size(); i++){ + Point A = pwd2_in(t_cuts[i]); + Point n = rot90(unit_vector(speed(t_cuts[i])))*sign; + if ((i % mminterval) == i_shift) { + output.concat (ruler_mark(A, n, MARK_MAJOR)); } else { - output.concat (ruler_mark(pwd2_arclength(t), n(t), MARK_MINOR)); + output.concat (ruler_mark(A, n, MARK_MINOR)); } } - if (border_marks == BORDERMARK_END || border_marks == BORDERMARK_BOTH) - output.concat (ruler_mark(B, n.lastValue(), MARK_MAJOR)); + //eventually draw a mark at start + if ((border_marks == BORDERMARK_START || border_marks == BORDERMARK_BOTH) && (offset != 0.0 || i_shift != 0)){ + Point A = pwd2_in.firstValue(); + Point n = rot90(unit_vector(speed.firstValue()))*sign; + output.concat (ruler_mark(A, n, MARK_MAJOR)); + } + //eventually draw a mark at end + if (border_marks == BORDERMARK_END || border_marks == BORDERMARK_BOTH){ + Point A = pwd2_in.lastValue(); + Point n = rot90(unit_vector(speed.lastValue()))*sign; + output.concat (ruler_mark(A, n, MARK_MAJOR)); + } return output; } diff --git a/src/live_effects/lpe-sketch.cpp b/src/live_effects/lpe-sketch.cpp index 6aebcd7fa..a95d89398 100644 --- a/src/live_effects/lpe-sketch.cpp +++ b/src/live_effects/lpe-sketch.cpp @@ -185,8 +185,12 @@ LPESketch::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_ parallel_offset.resetRandomizer(); strokelength_rdm.resetRandomizer(); strokeoverlap_rdm.resetRandomizer(); + ends_tolerance.resetRandomizer(); + tremble_size.resetRandomizer(); tgtlength_rdm.resetRandomizer(); + + // some variables for futur use (for construction lines; compute arclength only once...) // notations will be : t = path time, s = distance from start along the path. Piecewise<SBasis> pathlength; diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp index dd6c5a90a..aab62a75c 100644 --- a/src/live_effects/lpe-spiro.cpp +++ b/src/live_effects/lpe-spiro.cpp @@ -13,6 +13,7 @@ #include <2geom/matrix.h> #include <2geom/bezier-curve.h> #include <2geom/hvlinesegment.h> +#include <2geom/isnan.h> #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" @@ -25,6 +26,8 @@ #include "inkscape.h" #include "desktop.h" +#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS + typedef struct { bezctx base; SPCurve *curve; @@ -34,39 +37,55 @@ typedef struct { void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/) { bezctx_ink *bi = (bezctx_ink *) bc; - bi->curve->moveto(x, y); + 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; - bi->curve->lineto(x, y); + 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; - double x0, y0; - double x1, y1; - double x2, y2; - - Geom::Point last = *(bi->curve->last_point()); - x0 = last[Geom::X]; - y0 = last[Geom::Y]; - x1 = xm + (1./3) * (x0 - xm); - y1 = ym + (1./3) * (y0 - ym); - x2 = xm + (1./3) * (x3 - xm); - y2 = ym + (1./3) * (y3 - ym); - - bi->curve->curveto(x1, y1, x2, y2, x3, y3); + 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; - bi->curve->curveto(x1, y1, x2, y2, x3, y3); + 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 * @@ -213,20 +232,6 @@ LPESpiro::doEffect(SPCurve * curve) } g_free (path); - - // FIXME: refactor the spiro code such that it cannot generate non-continous paths! - // sometimes, the code above generates a path that is not continuous: caused by chaotic algorithm? - // The continuity error always happens after a lot of curveto calls (a big path probably that spins to infinity?) - // if so, undo the effect by resetting the original path. - try { - curve->get_pathvector() * Geom::identity(); // tests for continuity, this makes LPE Spiro slower of course :-( - } - catch (Geom::ContinuityError & e) { - g_warning("Exception during LPE Spiro execution. \n %s", e.what()); - SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE, - _("An exception occurred during execution of the Spiro Path Effect.") ); - curve->set_pathvector(original_pathv); - } } }; //namespace LivePathEffect diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index ef9d38ee4..72d96f5a2 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -7,58 +7,64 @@ */ #include <cstdio> - #include "live_effects/lpe-vonkoch.h" -#include "svg/svg.h" -#include "ui/widget/scalar.h" #include "nodepath.h" +#include <2geom/transforms.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 <algorithm> -using std::vector; - - +//using std::vector; namespace Inkscape { namespace LivePathEffect { -VonKochPathParam::~VonKochPathParam() -{ -} - void VonKochPathParam::param_setup_nodepath(Inkscape::NodePath::Path *np) -{ +{ PathParam::param_setup_nodepath(np); sp_nodepath_make_straight_path(np); } -static const Util::EnumData<VonKochRefType> VonKochRefTypeData[VKREF_END] = { - {VKREF_BBOX, N_("Bounding box"), "bbox"}, - {VKREF_SEG, N_("Last gen. segment"), "lastseg"}, -}; -static const Util::EnumDataConverter<VonKochRefType> VonKochRefTypeConverter(VonKochRefTypeData, VKREF_END); +//FIXME: a path is used here instead of 2 points to work around path/point param incompatibility bug. +void +VonKochRefPathParam::param_setup_nodepath(Inkscape::NodePath::Path *np) +{ + PathParam::param_setup_nodepath(np); + sp_nodepath_make_straight_path(np); +} +bool +VonKochRefPathParam::param_readSVGValue(const gchar * strvalue) +{ + std::vector<Geom::Path> old = _pathvector; + bool res = PathParam::param_readSVGValue(strvalue); + if (res && _pathvector.size()==1 && _pathvector.front().size()==1){ + return true; + }else{ + _pathvector = old; + return false; + } +} LPEVonKoch::LPEVonKoch(LivePathEffectObject *lpeobject) : Effect(lpeobject), + ref_path(_("Reference segment"), _("The reference segment. Defaults to bbox diameter."), "ref_path", &wr, this, "M0,0 L10,0"), + //refA(_("Ref Start"), _("Left side middle of the reference box"), "refA", &wr, this), + //refB(_("Ref End"), _("Right side middle of the reference box"), "refB", &wr, this), + generator(_("Generating path"), _("Path whose segments define the iterated transforms"), "generator", &wr, this, "M0,0 L30,0 M0,10 L10,10 M 20,10 L30,10"), + similar_only(_("Use uniform transforms only"), _("2 consecutive segments are used to reverse/preserve orientation only (otherwise, they define a general transform)."), "similar_only", &wr, this, false), 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"), - similar_only(_("Use uniform scale/rotation only"), _("If off, 2segments component of generating path can be used to define a general rtansform. If on, they only affect the orientation preserving/reversing of the transform."), "similar_only", &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), + //FIXME: a path is used here instead of 2 points to work around path/point param incompatibility bug. maxComplexity(_("Max complexity"), _("Disable effect if the output is too complex"), "maxComplexity", &wr, this, 1000) + //,draw_boxes(_("Display boxes"), _("Display boxes instead of paths only"), "draw_boxes", &wr, this, true) { + //FIXME: a path is used here instead of 2 points to work around path/point param incompatibility bug. + registerParameter( dynamic_cast<Parameter *>(&ref_path) ); + //registerParameter( dynamic_cast<Parameter *>(&refA) ); + //registerParameter( dynamic_cast<Parameter *>(&refB) ); registerParameter( dynamic_cast<Parameter *>(&generator) ); + registerParameter( dynamic_cast<Parameter *>(&similar_only) ); registerParameter( dynamic_cast<Parameter *>(&nbgenerations) ); registerParameter( dynamic_cast<Parameter *>(&drawall) ); - registerParameter( dynamic_cast<Parameter *>(&reftype) ); - registerParameter( dynamic_cast<Parameter *>(&similar_only) ); registerParameter( dynamic_cast<Parameter *>(&maxComplexity) ); + //registerParameter( dynamic_cast<Parameter *>(&draw_boxes) ); nbgenerations.param_make_integer(); nbgenerations.param_set_range(0, NR_HUGE); @@ -75,28 +81,27 @@ std::vector<Geom::Path> LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) { using namespace Geom; - std::vector<Geom::Path> generating_path = path_from_piecewise(generator.get_pwd2(),.01);//TODO what should that tolerance be? + + std::vector<Geom::Path> generating_path = generator.get_pathvector(); + + if (generating_path.size()==0) { + return path_in; + } //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 = Rect(boundingbox_X,boundingbox_Y); - m0 = Matrix(boundingbox_X.extent(),0,0,boundingbox_X.extent(), boundingbox_X.min(), boundingbox_Y.middle()); - - }else{ - if (generating_path.size()==0) return path_in; - Point p = generating_path.back().back().pointAt(0); - Point u = generating_path.back().back().pointAt(1)-p; - m0 = Matrix(u[X], u[Y],-u[Y], u[X], p[X], p[Y]); - } + Geom::Path refpath = ref_path.get_pathvector().front(); + Point A = refpath.pointAt(0); + Point B = refpath.pointAt(refpath.size()); + Point u = B-A; + m0 = Matrix(u[X], u[Y],-u[Y], u[X], A[X], A[Y]); + + //FIXME: a path is used as ref instead of 2 points to work around path/point param incompatibility bug. + //Point u = refB-refA; + //m0 = Matrix(u[X], u[Y],-u[Y], u[X], refA[X], refA[Y]); m0 = m0.inverse(); - std::vector<Matrix> transforms; - unsigned end = generating_path.size(); - if (type==VKREF_SEG) end-=1; for (unsigned i=0; i<generating_path.size(); i++){ Matrix m; if(generating_path[i].size()==1){ @@ -109,7 +114,7 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) Point p = generating_path[i].pointAt(1); Point u = generating_path[i].pointAt(2)-p; Point v = p-generating_path[i].pointAt(0); - if (similar_only){ + if (similar_only.get_value()){ int sign = (u[X]*v[Y]-u[Y]*v[X]>=0?1:-1); v[X] = -u[Y]*sign; v[Y] = u[X]*sign; @@ -120,13 +125,15 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) } } - if (transforms.size()==0) return path_in; + if (transforms.size()==0){ + return path_in; + } - //Do nothing if the output is too complex... + //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(); @@ -139,13 +146,14 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) complexity = pow(transforms.size(),nbgenerations)*path_in_complexity; } if (complexity > double(maxComplexity)){ + g_warning("VonKoch lpe's output too complex. Effect bypassed."); return path_in; } //Generate path: std::vector<Geom::Path> pathi = path_in; std::vector<Geom::Path> path_out = path_in; - + for (unsigned i = 0; i<nbgenerations; i++){ if (drawall.get_value()){ path_out = path_in; @@ -156,7 +164,7 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) } for (unsigned j = 0; j<transforms.size(); j++){ for (unsigned k = 0; k<pathi.size() && complexity < maxComplexity; k++){ - path_out.push_back(pathi[k]*transforms[j]); + path_out.push_back(pathi[k]*transforms[j]); complexity+=pathi[k].size(); } } @@ -165,10 +173,98 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) return path_out; } + +//Usefull?? +//void +//LPEVonKoch::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) +/*{ + using namespace Geom; + if (draw_boxes.get_value()){ + double ratio = .5; + if (similar_only.get_value()) ratio = boundingbox_Y.extent()/boundingbox_X.extent()/2; + + Point BB1,BB2,BB3,BB4,v; + + //Draw the reference box (ref_path is supposed to consist in one line segment) + //FIXME: a path is used as ref instead of 2 points to work around path/point param incompatibility bug. + Geom::Path refpath = ref_path.get_pathvector().front(); + if (refpath.size()==1){ + BB1 = BB4 = refpath.front().pointAt(0); + BB2 = BB3 = refpath.front().pointAt(1); + v = rot90(BB2 - BB1)*ratio; + BB1 -= v; + BB2 -= v; + BB3 += v; + BB4 += v; + Geom::Path refbox(BB1); + refbox.appendNew<LineSegment>(BB2); + refbox.appendNew<LineSegment>(BB3); + refbox.appendNew<LineSegment>(BB4); + refbox.close(); + PathVector refbox_as_vect; + refbox_as_vect.push_back(refbox); + hp_vec.push_back(refbox_as_vect); + } + //Draw the transformed boxes + std::vector<Geom::Path> generating_path = generator.get_pathvector(); + for (unsigned i=0;i<generating_path.size(); i++){ + if (generating_path[i].size()==0){ + //Ooops! this should not happen. + }else if (generating_path[i].size()==1){ + BB1 = BB4 = generating_path[i].pointAt(0); + BB2 = BB3 = generating_path[i].pointAt(1); + v = rot90(BB2 - BB1)*ratio; + }else{//Only tak the first 2 segments into account. + BB1 = BB4 = generating_path[i].pointAt(1); + BB2 = BB3 = generating_path[i].pointAt(2); + if(similar_only.get_value()){ + v = rot90(BB2 - BB1)*ratio; + }else{ + v = (generating_path[i].pointAt(0) - BB1)*ratio; + } + } + BB1 -= v; + BB2 -= v; + BB3 += v; + BB4 += v; + Geom::Path path(BB1); + path.appendNew<LineSegment>(BB2); + path.appendNew<LineSegment>(BB3); + path.appendNew<LineSegment>(BB4); + path.close(); + PathVector pathv; + pathv.push_back(path); + hp_vec.push_back(pathv); + } + } +} +*/ + void LPEVonKoch::doBeforeEffect (SPLPEItem *lpeitem) { + using namespace Geom; original_bbox(lpeitem); + + std::vector<Geom::Path> paths = ref_path.get_pathvector(); + Geom::Point A,B; + if (paths.size()==0||paths.front().size()==0){ + //FIXME: a path is used as ref instead of 2 points to work around path/point param incompatibility bug. + //refA.param_setValue( Geom::Point(boundingbox_X.min(), boundingbox_Y.middle()) ); + //refB.param_setValue( Geom::Point(boundingbox_X.max(), boundingbox_Y.middle()) ); + A = Point(boundingbox_X.min(), boundingbox_Y.middle()); + B = Point(boundingbox_X.max(), boundingbox_Y.middle()); + }else{ + A = paths.front().pointAt(0); + B = paths.front().pointAt(paths.front().size()); + } + if (paths.size()!=1||paths.front().size()!=1){ + Geom::Path tmp_path(A); + tmp_path.appendNew<LineSegment>(B); + std::vector<Geom::Path> tmp_pathv; + tmp_pathv.push_back(tmp_path); + ref_path.set_new_value(tmp_pathv,true); + } } @@ -177,17 +273,36 @@ LPEVonKoch::resetDefaults(SPItem * item) { using namespace Geom; original_bbox(SP_LPE_ITEM(item)); - Point start(boundingbox_X.min(), boundingbox_Y.middle()); - Point end(boundingbox_X.max(), boundingbox_Y.middle()); - std::vector<Geom::Path> paths; - Geom::Path path = Geom::Path(start); - path.appendNew<Geom::LineSegment>( end ); - paths.push_back(path * Matrix(1./3,0,0,1./3,start[X]*2./3,start[Y]*2./3 + boundingbox_Y.extent()/2)); - paths.push_back(path * Matrix(1./3,0,0,1./3, end[X]*2./3, end[Y]*2./3 + boundingbox_Y.extent()/2)); - paths.push_back(path); + Point A,B; + A[Geom::X] = boundingbox_X.min(); + A[Geom::Y] = boundingbox_Y.middle(); + B[Geom::X] = boundingbox_X.max(); + B[Geom::Y] = boundingbox_Y.middle(); + + std::vector<Geom::Path> paths,refpaths; + Geom::Path path = Geom::Path(A); + path.appendNew<Geom::LineSegment>(B); + refpaths.push_back(path); + ref_path.set_new_value(refpaths, true); + + paths.push_back(path * Matrix(1./3,0,0,1./3, A[X]*2./3, A[Y]*2./3 + boundingbox_Y.extent()/2)); + paths.push_back(path * Matrix(1./3,0,0,1./3, B[X]*2./3, B[Y]*2./3 + boundingbox_Y.extent()/2)); generator.set_new_value(paths, true); + + //FIXME: a path is used as ref instead of 2 points to work around path/point param incompatibility bug. + //refA[Geom::X] = boundingbox_X.min(); + //refA[Geom::Y] = boundingbox_Y.middle(); + //refB[Geom::X] = boundingbox_X.max(); + //refB[Geom::Y] = boundingbox_Y.middle(); + //std::vector<Geom::Path> paths; + //Geom::Path path = Geom::Path( (Point) refA); + //path.appendNew<Geom::LineSegment>( (Point) refB ); + //paths.push_back(path * Matrix(1./3,0,0,1./3, refA[X]*2./3, refA[Y]*2./3 + boundingbox_Y.extent()/2)); + //paths.push_back(path * Matrix(1./3,0,0,1./3, refB[X]*2./3, refB[Y]*2./3 + boundingbox_Y.extent()/2)); + //paths.push_back(path); + //generator.set_new_value(paths, true); } void diff --git a/src/live_effects/lpe-vonkoch.h b/src/live_effects/lpe-vonkoch.h index 011602eaf..16964f527 100644 --- a/src/live_effects/lpe-vonkoch.h +++ b/src/live_effects/lpe-vonkoch.h @@ -12,30 +12,38 @@ #include "live_effects/effect.h" #include "live_effects/lpegroupbbox.h" #include "live_effects/parameter/path.h" -#include "live_effects/parameter/enum.h" +#include "live_effects/parameter/point.h" #include "live_effects/parameter/bool.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); + 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); }; + //FIXME: a path is used here instead of 2 points to work around path/point param incompatibility bug. +class VonKochRefPathParam : public PathParam{ +public: + VonKochRefPathParam ( 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 ~VonKochRefPathParam(){} + virtual void param_setup_nodepath(Inkscape::NodePath::Path *np); + virtual bool param_readSVGValue(const gchar * strvalue); + }; + class LPEVonKoch : public Effect, GroupBBoxEffect { public: LPEVonKoch(LivePathEffectObject *lpeobject); @@ -49,16 +57,22 @@ public: virtual void transform_multiply(Geom::Matrix const& postmul, bool set); + //Usefull?? + // protected: + //virtual void addCanvasIndicators(SPLPEItem *lpeitem, std::vector<Geom::PathVector> &hp_vec); + private: ScalarParam nbgenerations; VonKochPathParam generator; BoolParam similar_only; BoolParam drawall; - EnumParam<VonKochRefType> reftype; + //BoolParam draw_boxes; + //FIXME: a path is used here instead of 2 points to work around path/point param incompatibility bug. + VonKochRefPathParam ref_path; + // PointParam refA; + // PointParam refB; ScalarParam maxComplexity; - //void on_pattern_pasted(); - LPEVonKoch(const LPEVonKoch&); LPEVonKoch& operator=(const LPEVonKoch&); }; diff --git a/src/live_effects/lpegroupbbox.cpp b/src/live_effects/lpegroupbbox.cpp index c11e99ecd..79e6857f0 100644 --- a/src/live_effects/lpegroupbbox.cpp +++ b/src/live_effects/lpegroupbbox.cpp @@ -27,7 +27,7 @@ GroupBBoxEffect::original_bbox(SPLPEItem *lpeitem, bool absolute) transform = Geom::identity(); } - Geom::Rect itemBBox = to_2geom(*item->getBounds(transform, SPItem::GEOMETRIC_BBOX)); // fixme: fix for when getBounds returns invalid Rect + Geom::Rect itemBBox = *item->getBounds(transform, SPItem::GEOMETRIC_BBOX); // fixme: fix for when getBounds returns invalid Rect boundingbox_X = itemBBox[Geom::X]; boundingbox_Y = itemBBox[Geom::Y]; } diff --git a/src/live_effects/lpeobject.cpp b/src/live_effects/lpeobject.cpp index ab49de14f..ec0dee0be 100644 --- a/src/live_effects/lpeobject.cpp +++ b/src/live_effects/lpeobject.cpp @@ -21,15 +21,6 @@ //#define LIVEPATHEFFECT_VERBOSE -static void livepatheffect_class_init(LivePathEffectObjectClass *klass); -static void livepatheffect_init(LivePathEffectObject *stop); - -static void livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static void livepatheffect_release(SPObject *object); - -static void livepatheffect_set(SPObject *object, unsigned key, gchar const *value); -static Inkscape::XML::Node *livepatheffect_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); - static void livepatheffect_on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data); static SPObjectClass *livepatheffect_parent_class; @@ -37,7 +28,7 @@ static SPObjectClass *livepatheffect_parent_class; * Registers the LivePathEffect class with Gdk and returns its type number. */ GType -livepatheffect_get_type () +LivePathEffectObject::livepatheffect_get_type () { static GType livepatheffect_type = 0; @@ -45,11 +36,11 @@ livepatheffect_get_type () GTypeInfo livepatheffect_info = { sizeof (LivePathEffectObjectClass), NULL, NULL, - (GClassInitFunc) livepatheffect_class_init, + (GClassInitFunc) LivePathEffectObject::livepatheffect_class_init, NULL, NULL, sizeof (LivePathEffectObject), 16, - (GInstanceInitFunc) livepatheffect_init, + (GInstanceInitFunc) LivePathEffectObject::livepatheffect_init, NULL, }; livepatheffect_type = g_type_register_static (SP_TYPE_OBJECT, "LivePathEffectObject", &livepatheffect_info, (GTypeFlags)0); @@ -69,8 +60,8 @@ static Inkscape::XML::NodeEventVector const livepatheffect_repr_events = { /** * Callback to initialize livepatheffect vtable. */ -static void -livepatheffect_class_init(LivePathEffectObjectClass *klass) +void +LivePathEffectObject::livepatheffect_class_init(LivePathEffectObjectClass *klass) { SPObjectClass *sp_object_class = (SPObjectClass *) klass; @@ -86,8 +77,8 @@ livepatheffect_class_init(LivePathEffectObjectClass *klass) /** * Callback to initialize livepatheffect object. */ -static void -livepatheffect_init(LivePathEffectObject *lpeobj) +void +LivePathEffectObject::livepatheffect_init(LivePathEffectObject *lpeobj) { #ifdef LIVEPATHEFFECT_VERBOSE g_message("Init livepatheffectobject"); @@ -101,8 +92,8 @@ livepatheffect_init(LivePathEffectObject *lpeobj) /** * Virtual build: set livepatheffect attributes from its associated XML node. */ -static void -livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +void +LivePathEffectObject::livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) { #ifdef LIVEPATHEFFECT_VERBOSE g_message("Build livepatheffect"); @@ -126,8 +117,8 @@ livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node /** * Virtual release of livepatheffect members before destruction. */ -static void -livepatheffect_release(SPObject *object) +void +LivePathEffectObject::livepatheffect_release(SPObject *object) { #ifdef LIVEPATHEFFECT_VERBOSE g_print("Releasing livepatheffect"); @@ -168,8 +159,8 @@ livepatheffect_release(SPObject *object) /** * Virtual set: set attribute to value. */ -static void -livepatheffect_set(SPObject *object, unsigned key, gchar const *value) +void +LivePathEffectObject::livepatheffect_set(SPObject *object, unsigned key, gchar const *value) { LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object); #ifdef LIVEPATHEFFECT_VERBOSE @@ -202,8 +193,8 @@ livepatheffect_set(SPObject *object, unsigned key, gchar const *value) /** * Virtual write: write object attributes to repr. */ -static Inkscape::XML::Node * -livepatheffect_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +Inkscape::XML::Node * +LivePathEffectObject::livepatheffect_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { #ifdef LIVEPATHEFFECT_VERBOSE g_print("Write livepatheffect"); @@ -243,10 +234,10 @@ livepatheffect_on_repr_attr_changed ( Inkscape::XML::Node * /*repr*/, return; LivePathEffectObject *lpeobj = (LivePathEffectObject*) data; - if (!lpeobj->lpe) + if (!lpeobj->get_lpe()) return; - lpeobj->lpe->setParameter(key, newval); + lpeobj->get_lpe()->setParameter(key, newval); lpeobj->requestModified(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/live_effects/lpeobject.h b/src/live_effects/lpeobject.h index f141f07ca..dc631a5c1 100644 --- a/src/live_effects/lpeobject.h +++ b/src/live_effects/lpeobject.h @@ -12,26 +12,49 @@ #include "sp-object.h" #include "effect.h" -#define TYPE_LIVEPATHEFFECT (livepatheffect_get_type()) +namespace Inkscape { +namespace XML { +class Node; +class Document; +} +} + +#define TYPE_LIVEPATHEFFECT (LivePathEffectObject::livepatheffect_get_type()) #define LIVEPATHEFFECT(o) (G_TYPE_CHECK_INSTANCE_CAST((o), TYPE_LIVEPATHEFFECT, LivePathEffectObject)) #define IS_LIVEPATHEFFECT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), TYPE_LIVEPATHEFFECT)) +/// The LivePathEffect vtable. +struct LivePathEffectObjectClass { + SPObjectClass parent_class; +}; + class LivePathEffectObject : public SPObject { public: Inkscape::LivePathEffect::EffectType effecttype; - Inkscape::LivePathEffect::Effect *lpe; bool effecttype_set; LivePathEffectObject * fork_private_if_necessary(unsigned int nr_of_allowed_users = 1); -}; -/// The LivePathEffect vtable. -struct LivePathEffectObjectClass { - SPObjectClass parent_class; + /* Note that the returned pointer can be NULL in a valid LivePathEffectObject contained in a valid list of lpeobjects in an lpeitem! + * So one should always check whether the returned value is NULL or not */ + Inkscape::LivePathEffect::Effect * get_lpe() { return lpe; }; + +private: + Inkscape::LivePathEffect::Effect *lpe; // this can be NULL in a valid LivePathEffectObject + + /* C-style class functions: */ +public: + static GType livepatheffect_get_type(); +private: + static void livepatheffect_class_init(LivePathEffectObjectClass *klass); + static void livepatheffect_init(LivePathEffectObject *stop); + static void livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); + static void livepatheffect_release(SPObject *object); + static void livepatheffect_set(SPObject *object, unsigned key, gchar const *value); + static Inkscape::XML::Node *livepatheffect_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); }; -GType livepatheffect_get_type(); #endif diff --git a/src/live_effects/parameter/pointparam-knotholder.cpp b/src/live_effects/parameter/pointparam-knotholder.cpp index 34c149b4b..b814f597d 100644 --- a/src/live_effects/parameter/pointparam-knotholder.cpp +++ b/src/live_effects/parameter/pointparam-knotholder.cpp @@ -117,7 +117,8 @@ static void pointparam_knot_moved_handler(SPKnot */*knot*/, Geom::Point const *p Inkscape::SVGOStringStream os; os << pos; - kh->lpeobject->lpe->setParameter(kh->repr_key, os.str().c_str()); + // note: get_lpe() will always return a valid pointer? + kh->lpeobject->get_lpe()->setParameter(kh->repr_key, os.str().c_str()); } static void pointparam_knot_ungrabbed_handler(SPKnot *knot, unsigned int /*state*/, PointParamKnotHolder *kh) |
