diff options
| author | Johan B. C. Engelen <jbc.engelen@swissonline.ch> | 2011-09-22 21:12:59 +0000 |
|---|---|---|
| committer | Johan Engelen <goejendaagh@zonnet.nl> | 2011-09-22 21:12:59 +0000 |
| commit | afeffc3d8531543a5a2c9d949f100456d518bd59 (patch) | |
| tree | b54702d0b42de4ed358d10dd2ebfaa818dfcdda3 /src | |
| parent | partial 2geom update, powerstroke wants it (diff) | |
| download | inkscape-afeffc3d8531543a5a2c9d949f100456d518bd59.tar.gz inkscape-afeffc3d8531543a5a2c9d949f100456d518bd59.zip | |
restructure powerstroke LPE a bit in preparation for cusp fixup
(bzr r10645)
Diffstat (limited to 'src')
| -rw-r--r-- | src/live_effects/lpe-powerstroke.cpp | 425 | ||||
| -rw-r--r-- | src/live_effects/lpe-powerstroke.h | 12 |
2 files changed, 253 insertions, 184 deletions
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index e622ea976..8884e8b21 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -21,7 +21,10 @@ #include <2geom/transforms.h> #include <2geom/bezier-utils.h> #include <2geom/svg-elliptical-arc.h> +#include <2geom/sbasis-to-bezier.h> +#include <2geom/svg-path.h> +// for the spiro interpolator: #include "live_effects/bezctx.h" #include "live_effects/bezctx_intf.h" #include "live_effects/spiro.h" @@ -319,7 +322,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : registerParameter( dynamic_cast<Parameter *>(&sort_points) ); registerParameter( dynamic_cast<Parameter *>(&interpolator_type) ); registerParameter( dynamic_cast<Parameter *>(&start_linecap_type) ); - //registerParameter( dynamic_cast<Parameter *>(&cusp_linecap_type) ); + registerParameter( dynamic_cast<Parameter *>(&cusp_linecap_type) ); registerParameter( dynamic_cast<Parameter *>(&end_linecap_type) ); } @@ -345,212 +348,268 @@ static bool compare_offsets (Geom::Point first, Geom::Point second) return first[Geom::X] < second[Geom::X]; } + // find discontinuities in piecewise +std::vector<unsigned> find_discontinuities(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, double eps=Geom::EPSILON) +{ + std::vector<unsigned> indices; + for(unsigned i = 1; i < pwd2_in.size(); i++) { + if ( ! are_near(pwd2_in[i-1].at1(), pwd2_in[i].at0(), eps) ) { + indices.push_back(i); + } + } + return indices; +} Geom::Piecewise<Geom::D2<Geom::SBasis> > -LPEPowerStroke::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) +LPEPowerStroke::doEffect_pwd2_open ( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & n ) { using namespace Geom; - offset_points.set_pwd2(pwd2_in); + Piecewise<D2<SBasis> > output; + + LineCapType start_linecap = static_cast<LineCapType>(start_linecap_type.get_value()); + LineCapType end_linecap = static_cast<LineCapType>(end_linecap_type.get_value()); - Piecewise<D2<SBasis> > der = unitVector(derivative(pwd2_in)); - Piecewise<D2<SBasis> > n = rot90(der); - offset_points.set_pwd2_normal(n); + // perhaps use std::list instead of std::vector? + std::vector<Geom::Point> ts(offset_points.data().size() + 2); + for (unsigned int i = 0; i < offset_points.data().size(); ++i) { + ts.at(i+1) = offset_points.data().at(i); + } + if (sort_points) { + sort(ts.begin()+1, ts.end()-1, compare_offsets); + } - // see if we should treat the path as being closed. - bool closed_path = false; - if ( are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) { - closed_path = true; + // first and last point have same distance from path as second and second to last points, respectively. + ts.front() = Point(pwd2_in.domain().min(), (*(ts.begin()+1))[Geom::Y] ); + ts.back() = Point(pwd2_in.domain().max(), (*(ts.end()-2))[Geom::Y] ); + + // create stroke path where points (x,y) := (t, offset) + Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())); + Geom::Path strokepath = interpolator->interpolateToPath(ts); + delete interpolator; + + D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb()); + Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]); + Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]); + + // find time values for which x lies outside path domain + // and only take portion of x and y that lies within those time values + std::vector< double > rtsmin = roots (x - pwd2_in.domain().min()); + std::vector< double > rtsmax = roots (x - pwd2_in.domain().max()); + if ( !rtsmin.empty() && !rtsmax.empty() ) { + x = portion(x, rtsmin.at(0), rtsmax.at(0)); + y = portion(y, rtsmin.at(0), rtsmax.at(0)); } - Piecewise<D2<SBasis> > output; - if (!closed_path) { - LineCapType start_linecap = static_cast<LineCapType>(start_linecap_type.get_value()); - LineCapType end_linecap = static_cast<LineCapType>(end_linecap_type.get_value()); - - // perhaps use std::list instead of std::vector? - std::vector<Geom::Point> ts(offset_points.data().size() + 2); - for (unsigned int i = 0; i < offset_points.data().size(); ++i) { - ts.at(i+1) = offset_points.data().at(i); + output = compose(pwd2_in,x) + y*compose(n,x); + + x = reverse(x); + y = reverse(y); + Piecewise<D2<SBasis> > mirrorpath = compose(pwd2_in,x) - y*compose(n,x); + + switch (end_linecap) { + case LINECAP_PEAK: + { + Geom::Point end_deriv = der.lastValue(); + double radius = 0.5 * distance(output.lastValue(), mirrorpath.firstValue()); + Geom::Point midpoint = 0.5*(output.lastValue() + mirrorpath.firstValue()) + radius*end_deriv; + Geom::LineSegment cap11(output.lastValue(), midpoint); + Geom::LineSegment cap12(midpoint, mirrorpath.firstValue()); + output.continuousConcat(Piecewise<D2<SBasis> >(cap11.toSBasis())); + output.continuousConcat(Piecewise<D2<SBasis> >(cap12.toSBasis())); + break; } - if (sort_points) { - sort(ts.begin()+1, ts.end()-1, compare_offsets); + case LINECAP_SQUARE: + { + Geom::Point end_deriv = der.lastValue(); + double radius = 0.5 * distance(output.lastValue(), mirrorpath.firstValue()); + Geom::LineSegment cap11(output.lastValue(), output.lastValue() + radius*end_deriv); + Geom::LineSegment cap12(output.lastValue() + radius*end_deriv, mirrorpath.firstValue() + radius*end_deriv); + Geom::LineSegment cap13(mirrorpath.firstValue() + radius*end_deriv, mirrorpath.firstValue()); + output.continuousConcat(Piecewise<D2<SBasis> >(cap11.toSBasis())); + output.continuousConcat(Piecewise<D2<SBasis> >(cap12.toSBasis())); + output.continuousConcat(Piecewise<D2<SBasis> >(cap13.toSBasis())); + break; } - - switch (start_linecap) { - /* - case LINECAP_SHARP: - // first and last point coincide with input path to make sharp points on ends - ts.front() = Point(pwd2_in.domain().min(),0); - break; - */ - case LINECAP_PEAK: - case LINECAP_SQUARE: - case LINECAP_BUTT: - case LINECAP_ROUND: - default: - // first and last point have same distance from path as second and second to last points, respectively. - ts.front() = Point(pwd2_in.domain().min(), (*(ts.begin()+1))[Geom::Y] ); - break; + case LINECAP_BUTT: + { + Geom::LineSegment cap1(output.lastValue(), mirrorpath.firstValue()); + output.continuousConcat(Piecewise<D2<SBasis> >(cap1.toSBasis())); + break; } - switch (end_linecap) { - /* - case LINECAP_SHARP: - ts.back() = Point(pwd2_in.domain().max(),0); - break; - */ - case LINECAP_PEAK: - case LINECAP_SQUARE: - case LINECAP_BUTT: - case LINECAP_ROUND: - default: - ts.back() = Point(pwd2_in.domain().max(), (*(ts.end()-2))[Geom::Y] ); - break; + case LINECAP_ROUND: + default: + { + double radius1 = 0.5 * distance(output.lastValue(), mirrorpath.firstValue()); + Geom::SVGEllipticalArc cap1(output.lastValue(), radius1, radius1, M_PI/2., false, y.firstValue() < 0, mirrorpath.firstValue()); // note that y is reversed above! + output.continuousConcat(Piecewise<D2<SBasis> >(cap1.toSBasis())); + break; } + } + + output.continuousConcat(mirrorpath); - // create stroke path where points (x,y) := (t, offset) - Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())); - Geom::Path strokepath = interpolator->interpolateToPath(ts); - delete interpolator; - - D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb()); - Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]); - Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]); - - // find time values for which x lies outside path domain - // and only take portion of x and y that lies within those time values - std::vector< double > rtsmin = roots (x - pwd2_in.domain().min()); - std::vector< double > rtsmax = roots (x - pwd2_in.domain().max()); - if ( !rtsmin.empty() && !rtsmax.empty() ) { - x = portion(x, rtsmin.at(0), rtsmax.at(0)); - y = portion(y, rtsmin.at(0), rtsmax.at(0)); + switch (start_linecap) { + case LINECAP_PEAK: + { + Geom::Point start_deriv = der.firstValue(); + double radius = 0.5 * distance(output.firstValue(), output.lastValue()); + Geom::Point midpoint = 0.5*(output.lastValue() + output.firstValue()) - radius*start_deriv; + Geom::LineSegment cap21(output.lastValue(), midpoint); + Geom::LineSegment cap22(midpoint, output.firstValue()); + output.continuousConcat(Piecewise<D2<SBasis> >(cap21.toSBasis())); + output.continuousConcat(Piecewise<D2<SBasis> >(cap22.toSBasis())); + break; + } + case LINECAP_SQUARE: + { + Geom::Point start_deriv = der.firstValue(); + double radius = 0.5 * distance(output.firstValue(), output.lastValue()); + Geom::LineSegment cap21(output.lastValue(), output.lastValue() - radius*start_deriv); + Geom::LineSegment cap22(output.lastValue() - radius*start_deriv, output.firstValue() - radius*start_deriv); + Geom::LineSegment cap23(output.firstValue() - radius*start_deriv, output.firstValue()); + output.continuousConcat(Piecewise<D2<SBasis> >(cap21.toSBasis())); + output.continuousConcat(Piecewise<D2<SBasis> >(cap22.toSBasis())); + output.continuousConcat(Piecewise<D2<SBasis> >(cap23.toSBasis())); + break; } + case LINECAP_BUTT: + { + Geom::LineSegment cap2(output.lastValue(), output.firstValue()); + output.continuousConcat(Piecewise<D2<SBasis> >(cap2.toSBasis())); + break; + } + case LINECAP_ROUND: + default: + { + double radius2 = 0.5 * distance(output.firstValue(), output.lastValue()); + Geom::SVGEllipticalArc cap2(output.lastValue(), radius2, radius2, M_PI/2., false, y.lastValue() < 0, output.firstValue()); // note that y is reversed above! + output.continuousConcat(Piecewise<D2<SBasis> >(cap2.toSBasis())); + break; + } + } - output = compose(pwd2_in,x) + y*compose(n,x); - x = reverse(x); - y = reverse(y); - Piecewise<D2<SBasis> > mirrorpath = compose(pwd2_in,x) - y*compose(n,x); - - switch (end_linecap) { - case LINECAP_PEAK: - { - Geom::Point end_deriv = der.lastValue(); - double radius = 0.5 * distance(output.lastValue(), mirrorpath.firstValue()); - Geom::Point midpoint = 0.5*(output.lastValue() + mirrorpath.firstValue()) + radius*end_deriv; - Geom::LineSegment cap11(output.lastValue(), midpoint); - Geom::LineSegment cap12(midpoint, mirrorpath.firstValue()); - output.continuousConcat(Piecewise<D2<SBasis> >(cap11.toSBasis())); - output.continuousConcat(Piecewise<D2<SBasis> >(cap12.toSBasis())); - break; - } - case LINECAP_SQUARE: - { - Geom::Point end_deriv = der.lastValue(); - double radius = 0.5 * distance(output.lastValue(), mirrorpath.firstValue()); - Geom::LineSegment cap11(output.lastValue(), output.lastValue() + radius*end_deriv); - Geom::LineSegment cap12(output.lastValue() + radius*end_deriv, mirrorpath.firstValue() + radius*end_deriv); - Geom::LineSegment cap13(mirrorpath.firstValue() + radius*end_deriv, mirrorpath.firstValue()); - output.continuousConcat(Piecewise<D2<SBasis> >(cap11.toSBasis())); - output.continuousConcat(Piecewise<D2<SBasis> >(cap12.toSBasis())); - output.continuousConcat(Piecewise<D2<SBasis> >(cap13.toSBasis())); - break; - } - case LINECAP_BUTT: - { - Geom::LineSegment cap1(output.lastValue(), mirrorpath.firstValue()); - output.continuousConcat(Piecewise<D2<SBasis> >(cap1.toSBasis())); - break; - } - case LINECAP_ROUND: - default: - { - double radius1 = 0.5 * distance(output.lastValue(), mirrorpath.firstValue()); - Geom::SVGEllipticalArc cap1(output.lastValue(), radius1, radius1, M_PI/2., false, y.firstValue() < 0, mirrorpath.firstValue()); // note that y is reversed above! - output.continuousConcat(Piecewise<D2<SBasis> >(cap1.toSBasis())); - break; - } + return output; +} + +Geom::Piecewise<Geom::D2<Geom::SBasis> > +LPEPowerStroke::doEffect_pwd2_closed ( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & /*der*/, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & n ) +{ + using namespace Geom; + + Piecewise<D2<SBasis> > output; + + // path is closed + // linecap parameter can be ignored + + // perhaps use std::list instead of std::vector? + std::vector<Geom::Point> ts = offset_points.data(); + if (sort_points) { + sort(ts.begin(), ts.end(), compare_offsets); + } + // add extra points for interpolation between first and last point + Point first_point = ts.front(); + Point last_point = ts.back(); + ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0)); + ts.push_back( first_point + Point(pwd2_in.domain().extent() ,0) ); + // create stroke path where points (x,y) := (t, offset) + Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())); + Geom::Path strokepath = interpolator->interpolateToPath(ts); + delete interpolator; + + // output 2 separate paths + D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb()); + Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]); + Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]); + // find time values for which x lies outside path domain + // and only take portion of x and y that lies within those time values + std::vector< double > rtsmin = roots (x - pwd2_in.domain().min()); + std::vector< double > rtsmax = roots (x - pwd2_in.domain().max()); + if ( !rtsmin.empty() && !rtsmax.empty() ) { + x = portion(x, rtsmin.at(0), rtsmax.at(0)); + y = portion(y, rtsmin.at(0), rtsmax.at(0)); + } + output = compose(pwd2_in,x) + y*compose(n,x); + x = reverse(x); + y = reverse(y); + output.concat(compose(pwd2_in,x) - y*compose(n,x)); + + return output; +} + +std::vector<Geom::Path> +LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in) +{ + using namespace Geom; + + std::vector<Geom::Path> path_out; + + for (unsigned int i=0; i < path_in.size(); i++) { + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb(); + + offset_points.set_pwd2(pwd2_in); + Piecewise<D2<SBasis> > der = unitVector(derivative(pwd2_in)); + Piecewise<D2<SBasis> > n = rot90(der); + offset_points.set_pwd2_normal(n); + + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out; + if (path_in[i].closed()) { + pwd2_out = doEffect_pwd2_closed(pwd2_in, der, n); + } else { + pwd2_out = doEffect_pwd2_open(pwd2_in, der, n); + } + + std::vector<Geom::Path> path = path_from_piecewise_fix_cusps( pwd2_out, LPE_CONVERSION_TOLERANCE); + // add the output path vector to the already accumulated vector: + for (unsigned int j=0; j < path.size(); j++) { + path_out.push_back(path[j]); } + } - output.continuousConcat(mirrorpath); - - switch (start_linecap) { - case LINECAP_PEAK: - { - Geom::Point start_deriv = der.firstValue(); - double radius = 0.5 * distance(output.firstValue(), output.lastValue()); - Geom::Point midpoint = 0.5*(output.lastValue() + output.firstValue()) - radius*start_deriv; - Geom::LineSegment cap21(output.lastValue(), midpoint); - Geom::LineSegment cap22(midpoint, output.firstValue()); - output.continuousConcat(Piecewise<D2<SBasis> >(cap21.toSBasis())); - output.continuousConcat(Piecewise<D2<SBasis> >(cap22.toSBasis())); - break; - } - case LINECAP_SQUARE: - { - Geom::Point start_deriv = der.firstValue(); - double radius = 0.5 * distance(output.firstValue(), output.lastValue()); - Geom::LineSegment cap21(output.lastValue(), output.lastValue() - radius*start_deriv); - Geom::LineSegment cap22(output.lastValue() - radius*start_deriv, output.firstValue() - radius*start_deriv); - Geom::LineSegment cap23(output.firstValue() - radius*start_deriv, output.firstValue()); - output.continuousConcat(Piecewise<D2<SBasis> >(cap21.toSBasis())); - output.continuousConcat(Piecewise<D2<SBasis> >(cap22.toSBasis())); - output.continuousConcat(Piecewise<D2<SBasis> >(cap23.toSBasis())); - break; + return path_out; +} + +std::vector<Geom::Path> +LPEPowerStroke::path_from_piecewise_fix_cusps(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol) { + +/* per definition, the input piecewise should be closed. each discontinuity should be fixed with a cusp-ending, + as defined by cusp_linecap_type +*/ + Geom::PathBuilder pb; + if(B.size() == 0) return pb.peek(); + Geom::Point start = B[0].at0(); + pb.moveTo(start); + for(unsigned i = 0; ; i++) { + if ( (i+1 == B.size()) + || !are_near(B[i+1].at0(), B[i].at1(), tol) ) + { + //start of a new path + if (are_near(start, B[i].at1()) && sbasis_size(B[i]) <= 1) { + pb.closePath(); + //last line seg already there (because of .closePath()) + goto no_add; } - case LINECAP_BUTT: - { - Geom::LineSegment cap2(output.lastValue(), output.firstValue()); - output.continuousConcat(Piecewise<D2<SBasis> >(cap2.toSBasis())); - break; + build_from_sbasis(pb, B[i], tol, false); + if (are_near(start, B[i].at1())) { + //it's closed, the last closing segment was not a straight line so it needed to be added, but still make it closed here with degenerate straight line. + pb.closePath(); } - case LINECAP_ROUND: - default: - { - double radius2 = 0.5 * distance(output.firstValue(), output.lastValue()); - Geom::SVGEllipticalArc cap2(output.lastValue(), radius2, radius2, M_PI/2., false, y.lastValue() < 0, output.firstValue()); // note that y is reversed above! - output.continuousConcat(Piecewise<D2<SBasis> >(cap2.toSBasis())); + no_add: + if (i+1 >= B.size()) { break; } + start = B[i+1].at0(); + pb.moveTo(start); + } else { + build_from_sbasis(pb, B[i], tol, false); } - - } else { - // path is closed - // linecap parameter can be ignored - - // perhaps use std::list instead of std::vector? - std::vector<Geom::Point> ts = offset_points.data(); - if (sort_points) { - sort(ts.begin(), ts.end(), compare_offsets); - } - // add extra points for interpolation between first and last point - Point first_point = ts.front(); - Point last_point = ts.back(); - ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0)); - ts.push_back( first_point + Point(pwd2_in.domain().extent() ,0) ); - // create stroke path where points (x,y) := (t, offset) - Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())); - Geom::Path strokepath = interpolator->interpolateToPath(ts); - delete interpolator; - - // output 2 separate paths - D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb()); - Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]); - Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]); - // find time values for which x lies outside path domain - // and only take portion of x and y that lies within those time values - std::vector< double > rtsmin = roots (x - pwd2_in.domain().min()); - std::vector< double > rtsmax = roots (x - pwd2_in.domain().max()); - if ( !rtsmin.empty() && !rtsmax.empty() ) { - x = portion(x, rtsmin.at(0), rtsmax.at(0)); - y = portion(y, rtsmin.at(0), rtsmax.at(0)); - } - output = compose(pwd2_in,x) + y*compose(n,x); - x = reverse(x); - y = reverse(y); - output.concat(compose(pwd2_in,x) - y*compose(n,x)); } - - return output; + pb.finish(); + return pb.peek(); } /* ######################## */ diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index 725c6c4cd..f941e844f 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -25,11 +25,21 @@ public: LPEPowerStroke(LivePathEffectObject *lpeobject); virtual ~LPEPowerStroke(); - virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); + virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); virtual void doOnApply(SPLPEItem *lpeitem); private: + Geom::Piecewise<Geom::D2<Geom::SBasis> > + doEffect_pwd2_open ( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & n ); + Geom::Piecewise<Geom::D2<Geom::SBasis> > + doEffect_pwd2_closed ( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der, + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & n ); + std::vector<Geom::Path> path_from_piecewise_fix_cusps(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol); + PowerStrokePointArrayParam offset_points; BoolParam sort_points; EnumParam<unsigned> interpolator_type; |
