summaryrefslogtreecommitdiffstats
path: root/src/live_effects/lpe-taperstroke.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects/lpe-taperstroke.cpp')
-rw-r--r--src/live_effects/lpe-taperstroke.cpp306
1 files changed, 156 insertions, 150 deletions
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
index 0e709f3ac..91b8fd7bc 100644
--- a/src/live_effects/lpe-taperstroke.cpp
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -232,52 +232,21 @@ Geom::Path return_at_first_cusp (Geom::Path const & path_in, double smooth_toler
return path_out;
}
-Geom::Curve * subdivide_at(const Geom::Curve* curve_in, Geom::Coord time, bool first)
-{
- //the only reason for this function is the lack of a subdivide function in the Curve class.
- //you have to cast to Beziers to be able to use subdivide(t)
- unsigned order = Outline::bezierOrder(curve_in);
- Geom::Curve* curve_out = curve_in->duplicate();
- switch (order)
- {
- //these need to be scoped because of the variable 'c'
- case 3:
- {
- Geom::CubicBezier c = first ? (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).first :
- (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).second;
- if (curve_out) delete curve_out;
- curve_out = c.duplicate();
- break;
- }
- case 2:
- {
- Geom::QuadraticBezier c = first ? (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).first :
- (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).second;
- if (curve_out) delete curve_out;
- curve_out = c.duplicate();
- break;
- }
- case 1:
- {
- Geom::BezierCurveN<1> c = first ? (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).first :
- (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).second;
- if (curve_out) delete curve_out;
- curve_out = c.duplicate();
- break;
- }
- }
- return curve_out;
-}
-
+Geom::Curve * subdivide_at(const Geom::Curve* curve_in, Geom::Coord time, bool first);
Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double width);
Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
{
+ Geom::Path first_cusp = return_at_first_cusp(path_in[0]);
+ Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse());
+
+ bool zeroStart = false;
+ bool zeroEnd = false;
//there is a pretty good chance that people will try to drag the knots
//on top of each other, so block it
unsigned size = path_in[0].size();
- if (size == return_at_first_cusp(path_in[0]).size()) {
+ if (size == first_cusp.size()) {
//check to see if the knots were dragged over each other
//if so, reset the end offset
if ( attach_start >= (size - attach_end) ) {
@@ -287,176 +256,213 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
//don't ever let it be zero
if (attach_start <= 0) {
- attach_start.param_set_value( 0.0001 );
+ attach_start.param_set_value( 0.00000001 );
}
if (attach_end <= 0) {
- attach_end.param_set_value( 0.0001 );
+ attach_end.param_set_value( 0.00000001 );
}
//don't let it be integer
if (double(unsigned(attach_start)) == attach_start) {
- attach_start.param_set_value(attach_start - 0.0001);
+ attach_start.param_set_value(attach_start - 0.00000001);
+ zeroStart = true;
}
if (double(unsigned(attach_end)) == attach_end) {
- attach_end.param_set_value(attach_end - 0.0001);
+ attach_end.param_set_value(attach_end - 0.00000001);
+ zeroStart = true;
}
- unsigned allowed_start = return_at_first_cusp(path_in[0]).size();
-
- unsigned allowed_end = return_at_first_cusp(path_in[0].reverse()).size();
+ unsigned allowed_start = first_cusp.size();
+ unsigned allowed_end = last_cusp.size();
+ //don't let the knots be farther than they are allowed to be
if ((unsigned)attach_start >= allowed_start) {
- attach_start.param_set_value((double)allowed_start - 0.0001);
+ attach_start.param_set_value((double)allowed_start - 0.00000001);
}
if ((unsigned)attach_end >= allowed_end) {
- attach_end.param_set_value((double)allowed_end - 0.0001);
+ attach_end.param_set_value((double)allowed_end - 0.00000001);
}
- //Path::operator () means get point at time t
- start_attach_point = return_at_first_cusp(path_in[0])(attach_start);
- end_attach_point = return_at_first_cusp(path_in[0].reverse())(attach_end);
+ //remember, Path::operator () means get point at time t
+ start_attach_point = first_cusp(attach_start);
+ end_attach_point = last_cusp(attach_end);
Geom::PathVector pathv_out;
+ //the following function just splits it up into three pieces.
pathv_out = doEffect_simplePath(path_in);
+ //now for the actual tapering. We use a Pattern Along Path method to get this done.
- //now for the fun stuff. Right? RIGHT?
-
- if (true) {
- Geom::PathVector real_pathv;
- Geom::Path real_path;
+ Geom::PathVector real_pathv;
+ Geom::Path real_path;
+ Geom::PathVector pat_vec;
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2;
+ Geom::Path throwaway_path;
+
+ if (!zeroStart) {
+ //Construct the pattern (pat_str stands for pattern string) (yes, this is easier, trust me)
+ std::stringstream pat_str;
+ pat_str << "M 1,0 1,1 C " << 1 - (double)smoothing << ",1 0,0.5 0,0.5 0,0.5 " << 1 - double(smoothing) << ",0 1,0";
+
+ pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
- //Construct the pattern (pat_str stands for pattern string)
- std::stringstream pat_str;
- pat_str << "M 1,0 1,1 C " << 1 - (double)smoothing << ",1 0,0.5 0,0.5 0,0.5 " << 1 - double(smoothing) << ",0 1,0";
-
- Geom::PathVector pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
+ pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], line_width));
- Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2;
- pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], line_width));
+ throwaway_path = Geom::path_from_piecewise(pwd2, 0.001)[0].reverse();
+ throwaway_path.erase_last(); //wtf
- Geom::Path throwaway_path = path_from_piecewise(pwd2, 0.001)[0].reverse();
- throwaway_path.erase_last();
-
- real_path.append(throwaway_path); //wtf
-
- throwaway_path = Outline::PathOutsideOutline(pathv_out[1],
- line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
+ real_path.append(throwaway_path);
+ }
+ //append the outside outline of the path (with direction)
+ throwaway_path = Outline::PathOutsideOutline(pathv_out[1],
+ line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
- throwaway_path.setInitial(real_path.finalPoint());
- real_path.append(throwaway_path);
-
- std::stringstream pat_str_1;
- pat_str_1 << "M 0,0 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0";
- pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
-
- pwd2 = Geom::Piecewise<Geom::D2<Geom::SBasis> > ();
- pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], line_width));
+ throwaway_path.setInitial(real_path.finalPoint());
+ real_path.append(throwaway_path);
+
+ if (!zeroEnd) {
+ //append the ending taper
+ std::stringstream pat_str_1;
+ pat_str_1 << "M 0,0 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0";
+ pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
- throwaway_path = Geom::Path();
- throwaway_path = path_from_piecewise(pwd2, 0.001)[0];
- throwaway_path.setInitial(real_path.finalPoint());
- real_path.append(throwaway_path);
+ pwd2 = Geom::Piecewise<Geom::D2<Geom::SBasis> > ();
+ pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], line_width));
- throwaway_path = Geom::Path();
- throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(),
- line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
-
- //throwaway_path = throwaway_path.reverse();
- throwaway_path.setInitial(real_path.finalPoint());
+ throwaway_path = Geom::path_from_piecewise(pwd2, 0.001)[0];
+ throwaway_path.setInitial(real_path.finalPoint());
+ real_path.append(throwaway_path);
+ }
+ //append the inside outline of the path (against direction)
+ throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(),
+ line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
- real_path.append(throwaway_path);
- //real_path.close();
+ throwaway_path.setInitial(real_path.finalPoint());
- real_pathv.push_back(real_path);
+ real_path.append(throwaway_path);
+ real_path.close();
- //real_pathv.push_back(path_from_piecewise(pwd2, 0.001)[0].reverse());
+ real_pathv.push_back(real_path);
- return real_pathv;
- }
-
- return pathv_out;
+ return real_pathv;
}
//in all cases, this should return a PathVector with three elements.
Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in)
{
- unsigned size = path_in[0].size();
+ unsigned size = path_in[0].size();
- //do subdivision and get out
- unsigned loc = (unsigned)attach_start;
- Geom::Curve * curve_start = path_in[0] [loc].duplicate();
+ //do subdivision and get out
+ unsigned loc = (unsigned)attach_start;
+ Geom::Curve * curve_start = path_in[0] [loc].duplicate();
- std::vector<Geom::Path> pathv_out;
- Geom::Path path_out = Geom::Path();
+ std::vector<Geom::Path> pathv_out;
+ Geom::Path path_out = Geom::Path();
- Geom::Path trimmed_start = Geom::Path();
- Geom::Path trimmed_end = Geom::Path();
+ Geom::Path trimmed_start = Geom::Path();
+ Geom::Path trimmed_end = Geom::Path();
- for (unsigned i = 0; i < loc; i++) {
- trimmed_start.append(path_in[0] [i]);
- }
+ for (unsigned i = 0; i < loc; i++) {
+ trimmed_start.append(path_in[0] [i]);
+ }
- #define OVERLAP 0 /*(0.001 / (line_width < 1 ? 1 : line_width))*/
- trimmed_start.append(*subdivide_at(curve_start, (attach_start - loc) + OVERLAP, true));
- curve_start = subdivide_at(curve_start, attach_start - loc, false);
+ trimmed_start.append(*subdivide_at(curve_start, (attach_start - loc), true));
+ curve_start = subdivide_at(curve_start, attach_start - loc, false);
- //special case: path is one segment long
- //special case: what if the two knots occupy the same segment?
- if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc ))
- {
- Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start);
- //it is just a dumb segment
- //we have to do some shifting here because the value changed when we reduced the length
- //of the previous segment.
- trimmed_end.append(*subdivide_at(curve_start, t - OVERLAP, false));
- for (unsigned j = (size - attach_end) + 1; j < size; j++) {
- trimmed_end.append(path_in[0] [j]);
- }
-
- curve_start = subdivide_at(curve_start, t, true);
- path_out.append(*curve_start);
- pathv_out.push_back(trimmed_start);
- pathv_out.push_back(path_out);
- pathv_out.push_back(trimmed_end);
- return pathv_out;
+ //special case: path is one segment long
+ //special case: what if the two knots occupy the same segment?
+ if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc ))
+ {
+ Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start);
+ //it is just a dumb segment
+ //we have to do some shifting here because the value changed when we reduced the length
+ //of the previous segment.
+ trimmed_end.append(*subdivide_at(curve_start, t, false));
+ for (unsigned j = (size - attach_end) + 1; j < size; j++) {
+ trimmed_end.append(path_in[0] [j]);
}
-
+
+ curve_start = subdivide_at(curve_start, t, true);
+ path_out.append(*curve_start);
pathv_out.push_back(trimmed_start);
+ pathv_out.push_back(path_out);
+ pathv_out.push_back(trimmed_end);
+ return pathv_out;
+ }
- //append almost all of the rest of the path, ignore the curves that the knot is past (we'll get to it in a minute)
- path_out.append(*curve_start);
+ pathv_out.push_back(trimmed_start);
+
+ //append almost all of the rest of the path, ignore the curves that the knot is past (we'll get to it in a minute)
+ path_out.append(*curve_start);
- for (unsigned k = loc + 1; k < (size - unsigned(attach_end)) - 1; k++) {
- path_out.append(path_in[0] [k]);
- }
+ for (unsigned k = loc + 1; k < (size - unsigned(attach_end)) - 1; k++) {
+ path_out.append(path_in[0] [k]);
+ }
- //deal with the last segment in a very similar fashion to the first
- loc = size - attach_end;
+ //deal with the last segment in a very similar fashion to the first
+ loc = size - attach_end;
- Geom::Curve * curve_end = path_in[0] [loc].duplicate();
+ Geom::Curve * curve_end = path_in[0] [loc].duplicate();
- Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
+ Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
- trimmed_end.append(*subdivide_at(curve_end, t - OVERLAP, false));
- curve_end = subdivide_at(curve_end, t, true);
+ trimmed_end.append(*subdivide_at(curve_end, t, false));
+ curve_end = subdivide_at(curve_end, t, true);
- for (unsigned j = (size - attach_end) + 1; j < size; j++) {
- trimmed_end.append(path_in[0] [j]);
- }
+ for (unsigned j = (size - attach_end) + 1; j < size; j++) {
+ trimmed_end.append(path_in[0] [j]);
+ }
- path_out.append(*curve_end);
- pathv_out.push_back(path_out);
+ path_out.append(*curve_end);
+ pathv_out.push_back(path_out);
- pathv_out.push_back(trimmed_end);
+ pathv_out.push_back(trimmed_end);
- if (curve_end) delete curve_end;
- if (curve_start) delete curve_start;
- return pathv_out;
+ if (curve_end) delete curve_end;
+ if (curve_start) delete curve_start;
+ return pathv_out;
+}
+
+Geom::Curve * subdivide_at(const Geom::Curve* curve_in, Geom::Coord time, bool first)
+{
+ //the only reason for this function is the lack of a subdivide function in the Curve class.
+ //you have to cast to Beziers to be able to use subdivide(t)
+ unsigned order = Outline::bezierOrder(curve_in);
+ Geom::Curve* curve_out = curve_in->duplicate();
+ switch (order)
+ {
+ //these need to be scoped because of the variable 'c'
+ case 3:
+ {
+ Geom::CubicBezier c = first ? (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).first :
+ (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).second;
+ if (curve_out) delete curve_out;
+ curve_out = c.duplicate();
+ break;
+ }
+ case 2:
+ {
+ Geom::QuadraticBezier c = first ? (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).first :
+ (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).second;
+ if (curve_out) delete curve_out;
+ curve_out = c.duplicate();
+ break;
+ }
+ case 1:
+ {
+ Geom::BezierCurveN<1> c = first ? (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).first :
+ (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).second;
+ if (curve_out) delete curve_out;
+ curve_out = c.duplicate();
+ break;
+ }
+ }
+ return curve_out;
}
+
//most of the below code is verbatim from Pattern Along Path. However, it needed a little
//tweaking to get it to work right in this case.
Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double prop_scale)