From d20b73d611b6de99e0e0697ee47a760de437ee97 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 2 Apr 2014 17:18:40 -0400 Subject: Clean up code (bzr r13090.1.42) --- src/live_effects/lpe-taperstroke.cpp | 905 ++++++++++++++++--------------- src/live_effects/lpe-taperstroke.h | 48 +- src/live_effects/pathoutlineprovider.cpp | 521 +++++++++--------- src/live_effects/pathoutlineprovider.h | 16 +- 4 files changed, 765 insertions(+), 725 deletions(-) mode change 100755 => 100644 src/live_effects/lpe-taperstroke.cpp mode change 100755 => 100644 src/live_effects/pathoutlineprovider.cpp (limited to 'src') diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp old mode 100755 new mode 100644 index b4e43209d..97ae02e3b --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -38,26 +38,27 @@ namespace Inkscape { namespace LivePathEffect { namespace TpS { - class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity { - public: - KnotHolderEntityAttachBegin(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {} - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; - }; - class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity { - public: - KnotHolderEntityAttachEnd(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {} - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; - }; + class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity { + public: + KnotHolderEntityAttachBegin(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {} + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual Geom::Point knot_get() const; + }; + + class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity { + public: + KnotHolderEntityAttachEnd(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {} + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual Geom::Point knot_get() const; + }; } // TpS static const Util::EnumData JoinType[] = { - {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"}, - {LINEJOIN_ROUND, N_("Rounded"), "round"}, - {LINEJOIN_REFLECTED, N_("Reflected"), "reflected"}, - {LINEJOIN_POINTY, N_("Miter"), "miter"}, - {LINEJOIN_EXTRAPOLATED, N_("Extrapolated"), "extrapolated"} + {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"}, + {LINEJOIN_ROUND, N_("Rounded"), "round"}, + {LINEJOIN_REFLECTED, N_("Reflected"), "reflected"}, + {LINEJOIN_POINTY, N_("Miter"), "miter"}, + {LINEJOIN_EXTRAPOLATED, N_("Extrapolated"), "extrapolated"} }; static const Util::EnumDataConverter JoinTypeConverter(JoinType, sizeof (JoinType)/sizeof(*JoinType)); @@ -65,31 +66,30 @@ static const Util::EnumDataConverter JoinTypeConverter(JoinType, sizeo LPETaperStroke::LPETaperStroke(LivePathEffectObject *lpeobject) : Effect(lpeobject), line_width(_("Stroke width"), _("The (non-tapered) width of the path"), "stroke_width", &wr, this, 3), - attach_start(_("Start offset"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2), - attach_end(_("End offset"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2), - smoothing(_("Taper smoothing"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5), - join_type(_("Join type"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED), - miter_limit(_("Miter limit"), _("Limit for miter joins"), "miter_limit", &wr, this, 30.) + attach_start(_("Start offset"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2), + attach_end(_("End offset"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2), + smoothing(_("Taper smoothing"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5), + join_type(_("Join type"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED), + miter_limit(_("Miter limit"), _("Limit for miter joins"), "miter_limit", &wr, this, 30.) { - /* uncomment the following line to have the original path displayed while the item is selected */ show_orig_path = true; - _provides_knotholder_entities = true; - - attach_start.param_set_digits(3); - attach_end.param_set_digits(3); - - - registerParameter( dynamic_cast(&line_width) ); - registerParameter( dynamic_cast(&attach_start) ); - registerParameter( dynamic_cast(&attach_end) ); - registerParameter( dynamic_cast(&smoothing) ); - registerParameter( dynamic_cast(&join_type) ); - registerParameter( dynamic_cast(&miter_limit) ); + _provides_knotholder_entities = true; + + attach_start.param_set_digits(3); + attach_end.param_set_digits(3); + + + registerParameter( dynamic_cast(&line_width) ); + registerParameter( dynamic_cast(&attach_start) ); + registerParameter( dynamic_cast(&attach_end) ); + registerParameter( dynamic_cast(&smoothing) ); + registerParameter( dynamic_cast(&join_type) ); + registerParameter( dynamic_cast(&miter_limit) ); } LPETaperStroke::~LPETaperStroke() { - + } //from LPEPowerStroke -- sets fill if stroke color because we will @@ -97,10 +97,10 @@ LPETaperStroke::~LPETaperStroke() void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) { - if (SP_IS_SHAPE(lpeitem)) { + if (SP_IS_SHAPE(lpeitem)) { SPLPEItem* item = const_cast(lpeitem); double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; - + SPCSSAttr *css = sp_repr_css_attr_new (); if (lpeitem->style->stroke.isSet()) { if (lpeitem->style->stroke.isPaintserver()) { @@ -122,13 +122,13 @@ void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) } else { sp_repr_css_unset_property (css, "fill"); } - + sp_repr_css_set_property(css, "stroke", "none"); - + sp_desktop_apply_css_recursive(item, css, true); sp_repr_css_attr_unref (css); - line_width.param_set_value(width); + line_width.param_set_value(width); } else { g_warning("LPE Join Type can only be applied to paths (not groups)."); } @@ -138,8 +138,10 @@ void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem) { - - if (SP_IS_SHAPE(lpeitem)) { + + if (SP_IS_SHAPE(lpeitem)) { + //TODO: make it getobjbyrepr instead of const_cast because this can cause + //undefined behavior SPLPEItem *item = const_cast(lpeitem); SPCSSAttr *css = sp_repr_css_attr_new (); @@ -164,7 +166,7 @@ void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem) sp_repr_css_unset_property (css, "stroke"); } - Inkscape::CSSOStringStream os; + Inkscape::CSSOStringStream os; os << fabs(line_width); sp_repr_css_set_property (css, "stroke-width", os.str().c_str()); @@ -173,335 +175,347 @@ void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem) sp_desktop_apply_css_recursive(item, css, true); sp_repr_css_attr_unref (css); item->updateRepr(); - } + } } //actual effect impl here Geom::Path return_at_first_cusp (Geom::Path const & path_in, double smooth_tolerance = 0.05) { - Geom::Path path_out = Geom::Path(); - - for (unsigned i = 0; i < path_in.size(); i++) - { - path_out.append(path_in[i]); - if (path_in.size() == 1) - break; - - //determine order of curve - int order = Outline::bezierOrder(&path_in[i]); - - Geom::Point start_point; - Geom::Point cross_point = path_in[i].finalPoint(); - Geom::Point end_point; - - g_assert(path_in[i].finalPoint() == path_in[i+1].initialPoint()); - - //can you tell that the following expressions have been shaped by - //repeated compiler errors? ;) - switch (order) - { - case 3: - start_point = (static_cast(&path_in[i]))->operator[] (2); - //major league b***f***ing - if (are_near(start_point, cross_point, 0.0000001)) { - start_point = (static_cast(&path_in[i]))->operator[] (1); - } - break; - case 2: - //this never happens - start_point = (static_cast(&path_in[i]))->operator[] (1); - break; - case 1: - default: - start_point = path_in[i].initialPoint(); - } - - order = Outline::bezierOrder(&path_in[i+1]); - - switch (order) - { - case 3: - end_point = (static_cast(&path_in[i+1]))->operator[] (1); - if (are_near(end_point, cross_point, 0.0000001)) { - end_point = (static_cast(&path_in[i+1]))->operator[] (2); - } - break; - case 2: - end_point = (static_cast(&path_in[i+1]))->operator[] (1); - break; - case 1: - default: - end_point = path_in[i+1].finalPoint(); - } - - g_assert(!are_near(start_point, cross_point, 0.0000001)); //take that motherf*ckers - g_assert(!are_near(cross_point, end_point, 0.0000001)); - g_assert(!are_near(start_point, end_point, 0.0000001)); - - if (!are_collinear(start_point, cross_point, end_point, smooth_tolerance)) - break; - } - return path_out; + Geom::Path path_out = Geom::Path(); + + for (unsigned i = 0; i < path_in.size(); i++) { + path_out.append(path_in[i]); + if (path_in.size() == 1) + break; + + //determine order of curve + int order = Outline::bezierOrder(&path_in[i]); + + Geom::Point start_point; + Geom::Point cross_point = path_in[i].finalPoint(); + Geom::Point end_point; + + g_assert(path_in[i].finalPoint() == path_in[i+1].initialPoint()); + + //can you tell that the following expressions have been shaped by + //repeated compiler errors? ;) + switch (order) { + case 3: + start_point = (static_cast(&path_in[i]))->operator[] (2); + //major league b***f***ing + if (are_near(start_point, cross_point, 0.0000001)) { + start_point = (static_cast(&path_in[i]))->operator[] (1); + } + break; + case 2: + //this never happens + start_point = (static_cast(&path_in[i]))->operator[] (1); + break; + case 1: + default: + start_point = path_in[i].initialPoint(); + } + + order = Outline::bezierOrder(&path_in[i+1]); + + switch (order) { + case 3: + end_point = (static_cast(&path_in[i+1]))->operator[] (1); + if (are_near(end_point, cross_point, 0.0000001)) { + end_point = (static_cast(&path_in[i+1]))->operator[] (2); + } + break; + case 2: + end_point = (static_cast(&path_in[i+1]))->operator[] (1); + break; + case 1: + default: + end_point = path_in[i+1].finalPoint(); + } + + //clearly it's collinear if two occupy the same point + g_assert(!are_near(start_point, cross_point, 0.0000001)); + g_assert(!are_near(cross_point, end_point, 0.0000001)); + g_assert(!are_near(start_point, end_point, 0.0000001)); + + if (!are_collinear(start_point, cross_point, end_point, smooth_tolerance)) + break; + } + return path_out; } Geom::Piecewise > stretch_along(Geom::Piecewise > pwd2_in, Geom::Path pattern, double width); +//references to pointers, because magic +void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second); 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 == 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) ) { - attach_end.param_set_value( size - attach_start ); - } - } - - //don't ever let it be zero - if (attach_start <= 0.00000001) { - attach_start.param_set_value( 0.00000001 ); - zeroStart = true; - } - if (attach_end <= 0.00000001) { - attach_end.param_set_value( 0.00000001 ); - zeroEnd = true; - } - - //don't let it be integer - if (double(unsigned(attach_start)) == attach_start) { - attach_start.param_set_value(attach_start - 0.00001); - } - if (double(unsigned(attach_end)) == attach_end) { - attach_end.param_set_value(attach_end - 0.00001); - } - - 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.00000001); - } - if ((unsigned)attach_end >= allowed_end) { - attach_end.param_set_value((double)allowed_end - 0.00000001); - } - - //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. - - Geom::PathVector real_pathv; - Geom::Path real_path; - Geom::PathVector pat_vec; - Geom::Piecewise > 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 C " << 1 - (double)smoothing << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)smoothing << ",1 1,1"; - - pat_vec = sp_svg_read_pathv(pat_str.str().c_str()); - pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], -fabs(line_width))); - throwaway_path = Geom::path_from_piecewise(pwd2, 0.001)[0]; - - real_path.append(throwaway_path); - } - //append the outside outline of the path (with direction) - throwaway_path = Outline::PathOutsideOutline(pathv_out[1], - -fabs(line_width), static_cast(join_type.get_value()), miter_limit); - - if (!zeroStart) { - throwaway_path.setInitial(real_path.finalPoint()); - real_path.append(throwaway_path); - } else { - real_path.append(throwaway_path, Geom::Path::STITCH_DISCONTINUOUS); - } - - 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()); - - pwd2 = Geom::Piecewise > (); - pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], -fabs(line_width))); - - 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(), - -fabs(line_width), static_cast(join_type.get_value()), miter_limit); - - if (!zeroEnd) { - //throwaway_path.setInitial(real_path.finalPoint()); - real_path.append(throwaway_path, Geom::Path::STITCH_DISCONTINUOUS); - } else { - real_path.append(throwaway_path, Geom::Path::STITCH_DISCONTINUOUS); - } - real_path.close(); - - real_pathv.push_back(real_path); - - return real_pathv; + 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 == 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) ) { + attach_end.param_set_value( size - attach_start ); + } + } + + //don't ever let it be zero + if (attach_start <= 0.00000001) { + attach_start.param_set_value( 0.00000001 ); + zeroStart = true; + } + if (attach_end <= 0.00000001) { + attach_end.param_set_value( 0.00000001 ); + zeroEnd = true; + } + + //don't let it be integer + if (double(unsigned(attach_start)) == attach_start) { + attach_start.param_set_value(attach_start - 0.00001); + } + if (double(unsigned(attach_end)) == attach_end) { + attach_end.param_set_value(attach_end - 0.00001); + } + + 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.00000001); + } + if ((unsigned)attach_end >= allowed_end) { + attach_end.param_set_value((double)allowed_end - 0.00000001); + } + + //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. + + Geom::PathVector real_pathv; + Geom::Path real_path; + Geom::PathVector pat_vec; + Geom::Piecewise > 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 C " << 1 - (double)smoothing << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)smoothing << ",1 1,1"; + + pat_vec = sp_svg_read_pathv(pat_str.str().c_str()); + pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], -fabs(line_width))); + throwaway_path = Geom::path_from_piecewise(pwd2, 0.001)[0]; + + real_path.append(throwaway_path); + } + //append the outside outline of the path (with direction) + throwaway_path = Outline::PathOutsideOutline(pathv_out[1], + -fabs(line_width), static_cast(join_type.get_value()), miter_limit); + + if (!zeroStart) { + throwaway_path.setInitial(real_path.finalPoint()); + real_path.append(throwaway_path); + } else { + real_path.append(throwaway_path, Geom::Path::STITCH_DISCONTINUOUS); + } + + 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()); + + pwd2 = Geom::Piecewise > (); + pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], -fabs(line_width))); + + 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(), + -fabs(line_width), static_cast(join_type.get_value()), miter_limit); + + if (!zeroEnd) { + //throwaway_path.setInitial(real_path.finalPoint()); + real_path.append(throwaway_path, Geom::Path::STITCH_DISCONTINUOUS); + } else { + real_path.append(throwaway_path, Geom::Path::STITCH_DISCONTINUOUS); + } + + //hmm + real_path.setFinal(real_path.initialPoint()); + + real_path.close(); + + real_pathv.push_back(real_path); + + 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(); - - //do subdivision and get out - unsigned loc = (unsigned)attach_start; - Geom::Curve * curve_start = path_in[0] [loc].duplicate(); - - std::vector pathv_out; - Geom::Path path_out = 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]); - } - - - //this is pretty annoying - //previously I wrote a function for this but it wasted a lot of time - //so I optimized it back into here. - unsigned order = Outline::bezierOrder(curve_start); - switch (order) { - case 3: { - Geom::CubicBezier *cb = static_cast(curve_start); - std::pair cb_pair = cb->subdivide((attach_start - loc)); - trimmed_start.append(cb_pair.first); curve_start = cb_pair.second.duplicate(); //goes out of scope - break; - } - case 2: { - Geom::QuadraticBezier *qb = static_cast(curve_start); - std::pair qb_pair = qb->subdivide((attach_start - loc)); - trimmed_start.append(qb_pair.first); curve_start = qb_pair.second.duplicate(); - break; - } - case 1: { - Geom::BezierCurveN<1> *lb = static_cast * >(curve_start); - std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide((attach_start - loc)); - trimmed_start.append(lb_pair.first); curve_start = lb_pair.second.duplicate(); - break; - } - } - - //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. - - order = Outline::bezierOrder(curve_start); - switch (order) { - case 3: { - Geom::CubicBezier *cb = static_cast(curve_start); - std::pair cb_pair = cb->subdivide(t); - trimmed_end.append(cb_pair.second); curve_start = cb_pair.first.duplicate(); - break; - } - case 2: { - Geom::QuadraticBezier *qb = static_cast(curve_start); - std::pair qb_pair = qb->subdivide(t); - trimmed_end.append(qb_pair.second); curve_start = qb_pair.first.duplicate(); - break; - } - case 1: { - Geom::BezierCurveN<1> *lb = static_cast * >(curve_start); - std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide(t); - trimmed_end.append(lb_pair.second); curve_start = lb_pair.first.duplicate(); - break; - } - } - - for (unsigned j = (size - attach_end) + 1; j < size; j++) { - trimmed_end.append(path_in[0] [j]); - } - - 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; - } - - 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]); - } - - //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::Coord t = Geom::nearest_point(end_attach_point, *curve_end); - - order = Outline::bezierOrder(curve_end); - switch (order) { - case 3: { - Geom::CubicBezier *cb = static_cast(curve_end); - std::pair cb_pair = cb->subdivide(t); - trimmed_end.append(cb_pair.second); curve_end = cb_pair.first.duplicate(); - break; - } - case 2: { - Geom::QuadraticBezier *qb = static_cast(curve_end); - std::pair qb_pair = qb->subdivide(t); - trimmed_end.append(qb_pair.second); curve_end = qb_pair.first.duplicate(); - break; - } - case 1: { - Geom::BezierCurveN<1> *lb = static_cast * >(curve_end); - std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide(t); - trimmed_end.append(lb_pair.second); curve_end = lb_pair.first.duplicate(); - break; - } - } - - 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); - - pathv_out.push_back(trimmed_end); - - if (curve_end) delete curve_end; - if (curve_start) delete curve_start; - return pathv_out; + 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(); + + std::vector pathv_out; + Geom::Path path_out = 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]); + } + + + //this is pretty annoying + //previously I wrote a function for this but it wasted a lot of time + //so I optimized it back into here. + unsigned order = Outline::bezierOrder(curve_start); + switch (order) { + case 3: { + Geom::CubicBezier *cb = static_cast(curve_start); + std::pair cb_pair = cb->subdivide((attach_start - loc)); + trimmed_start.append(cb_pair.first); + curve_start = cb_pair.second.duplicate(); //goes out of scope + break; + } + case 2: { + Geom::QuadraticBezier *qb = static_cast(curve_start); + std::pair qb_pair = qb->subdivide((attach_start - loc)); + trimmed_start.append(qb_pair.first); + curve_start = qb_pair.second.duplicate(); + break; + } + case 1: { + Geom::BezierCurveN<1> *lb = static_cast * >(curve_start); + std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide((attach_start - loc)); + trimmed_start.append(lb_pair.first); + curve_start = lb_pair.second.duplicate(); + break; + } + } + + //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. + + order = Outline::bezierOrder(curve_start); + switch (order) { + case 3: { + Geom::CubicBezier *cb = static_cast(curve_start); + std::pair cb_pair = cb->subdivide(t); + trimmed_end.append(cb_pair.second); + curve_start = cb_pair.first.duplicate(); + break; + } + case 2: { + Geom::QuadraticBezier *qb = static_cast(curve_start); + std::pair qb_pair = qb->subdivide(t); + trimmed_end.append(qb_pair.second); + curve_start = qb_pair.first.duplicate(); + break; + } + case 1: { + Geom::BezierCurveN<1> *lb = static_cast * >(curve_start); + std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide(t); + trimmed_end.append(lb_pair.second); + curve_start = lb_pair.first.duplicate(); + break; + } + } + + for (unsigned j = (size - attach_end) + 1; j < size; j++) { + trimmed_end.append(path_in[0] [j]); + } + + 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; + } + + 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]); + } + + //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::Coord t = Geom::nearest_point(end_attach_point, *curve_end); + + order = Outline::bezierOrder(curve_end); + switch (order) { + case 3: { + Geom::CubicBezier *cb = static_cast(curve_end); + std::pair cb_pair = cb->subdivide(t); + trimmed_end.append(cb_pair.second); + curve_end = cb_pair.first.duplicate(); + break; + } + case 2: { + Geom::QuadraticBezier *qb = static_cast(curve_end); + std::pair qb_pair = qb->subdivide(t); + trimmed_end.append(qb_pair.second); + curve_end = qb_pair.first.duplicate(); + break; + } + case 1: { + Geom::BezierCurveN<1> *lb = static_cast * >(curve_end); + std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide(t); + trimmed_end.append(lb_pair.second); + curve_end = lb_pair.first.duplicate(); + break; + } + } + + 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); + + pathv_out.push_back(trimmed_end); + + if (curve_end) delete curve_end; + if (curve_start) delete curve_start; + return pathv_out; } @@ -509,14 +523,14 @@ Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & pa //tweaking to get it to work right in this case. Geom::Piecewise > stretch_along(Geom::Piecewise > pwd2_in, Geom::Path pattern, double prop_scale) { - using namespace Geom; + using namespace Geom; // Don't allow empty path parameter: if ( pattern.empty() ) { return pwd2_in; } -/* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */ + /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */ Piecewise > output; std::vector > > pre_output; @@ -548,7 +562,7 @@ Geom::Piecewise > stretch_along(Geom::Piecewise > > paths_in; paths_in = split_at_discontinuities(pwd2_in); - for (unsigned idx = 0; idx < paths_in.size(); idx++){ + for (unsigned idx = 0; idx < paths_in.size(); idx++) { Geom::Piecewise > path_i = paths_in[idx]; Piecewise x = x0; Piecewise y = y0; @@ -556,14 +570,14 @@ Geom::Piecewise > stretch_along(Geom::Piecewise > n = rot90(derivative(uskeleton)); n = force_continuity(remove_short_cuts(n,.1)); - + int nbCopies = 0; double scaling = 1; nbCopies = 1; scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent(); double pattWidth = pattBndsX->extent() * scaling; - + if (scaling != 1.0) { x*=scaling; } @@ -573,20 +587,20 @@ Geom::Piecewise > stretch_along(Geom::Piecewise > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs); std::vector > > splited_output_piece = split_at_discontinuities(output_piece); pre_output.insert(pre_output.end(), splited_output_piece.begin(), splited_output_piece.end() ); - }else{ + } else { output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs)); } offs+=pattWidth; } } - /*if (false){ + /*if (false){ pre_output = fuse_nearby_ends(pre_output, fuse_tolerance); for (unsigned i=0; i > stretch_along(Geom::Piecewise(curve_in); + std::pair cb_pair = cb->subdivide(t); + //trimmed_start.append(cb_pair.first); + val_first = cb_pair.first.duplicate(); + val_second = cb_pair.second.duplicate(); + break; + } + case 2: { + Geom::QuadraticBezier *qb = static_cast(curve_in); + std::pair qb_pair = qb->subdivide(t); + //trimmed_start.append(qb_pair.first); + val_first = qb_pair.first.duplicate(); + val_second = qb_pair.second.duplicate(); + break; + } + case 1: { + Geom::BezierCurveN<1> *lb = static_cast * >(curve_in); + std::pair, Geom::BezierCurveN<1> > lb_pair = lb->subdivide(t); + //trimmed_start.append(lb_pair.first); + val_first = lb_pair.first.duplicate(); + val_second = lb_pair.second.duplicate(); + break; + } + } +} -void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) + +void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { - { - KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE ); - knotholder->add(e); - } - { - KnotHolderEntity *e = new TpS::KnotHolderEntityAttachEnd(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE ); - knotholder->add(e); - } + { + KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this); + e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE ); + knotholder->add(e); + } + { + KnotHolderEntity *e = new TpS::KnotHolderEntityAttachEnd(this); + e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE ); + knotholder->add(e); + } } namespace TpS { - void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) - { - using namespace Geom; - - LPETaperStroke* lpe = dynamic_cast(_effect); - - Geom::Point const s = snap_knot_position(p, state); - - SPCurve *curve = SP_PATH(item)->get_curve_for_edit(); - Geom::PathVector pathv = curve->get_pathvector(); - Piecewise > pwd2; - Geom::Path p_in = return_at_first_cusp(pathv[0]); - pwd2.concat(p_in.toPwSb()); - std::vector > > pwd_vec = split_at_discontinuities(pwd2); - - double t0 = nearest_point(s, pwd_vec[0]); - lpe->attach_start.param_set_value(t0); - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); - } - void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state) - { - using namespace Geom; - - LPETaperStroke* lpe = dynamic_cast(_effect); - - Geom::Point const s = snap_knot_position(p, state); - - SPCurve *curve = SP_PATH(item)->get_curve_for_edit(); - Geom::PathVector pathv = curve->get_pathvector(); - Piecewise > pwd2; - Geom::Path p_in = return_at_first_cusp(pathv[0].reverse()); - pwd2.concat(p_in.toPwSb()); - std::vector > > pwd_vec = split_at_discontinuities(pwd2); - - double t0 = nearest_point(s, pwd_vec[0]); - lpe->attach_end.param_set_value(t0); - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); - } - Geom::Point KnotHolderEntityAttachBegin::knot_get() const - { - LPETaperStroke const * lpe = dynamic_cast (_effect); - return lpe->start_attach_point; - } - Geom::Point KnotHolderEntityAttachEnd::knot_get() const - { - LPETaperStroke const * lpe = dynamic_cast (_effect); - return lpe->end_attach_point; - } +void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) +{ + using namespace Geom; + + LPETaperStroke* lpe = dynamic_cast(_effect); + + Geom::Point const s = snap_knot_position(p, state); + + SPCurve *curve = SP_PATH(item)->get_curve_for_edit(); + Geom::PathVector pathv = curve->get_pathvector(); + Piecewise > pwd2; + Geom::Path p_in = return_at_first_cusp(pathv[0]); + pwd2.concat(p_in.toPwSb()); + std::vector > > pwd_vec = split_at_discontinuities(pwd2); + + double t0 = nearest_point(s, pwd_vec[0]); + lpe->attach_start.param_set_value(t0); + + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); +} +void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state) +{ + using namespace Geom; + + LPETaperStroke* lpe = dynamic_cast(_effect); + + Geom::Point const s = snap_knot_position(p, state); + + SPCurve *curve = SP_PATH(item)->get_curve_for_edit(); + Geom::PathVector pathv = curve->get_pathvector(); + Piecewise > pwd2; + Geom::Path p_in = return_at_first_cusp(pathv[0].reverse()); + pwd2.concat(p_in.toPwSb()); + std::vector > > pwd_vec = split_at_discontinuities(pwd2); + + double t0 = nearest_point(s, pwd_vec[0]); + lpe->attach_end.param_set_value(t0); + + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); +} +Geom::Point KnotHolderEntityAttachBegin::knot_get() const +{ + LPETaperStroke const * lpe = dynamic_cast (_effect); + return lpe->start_attach_point; +} +Geom::Point KnotHolderEntityAttachEnd::knot_get() const +{ + LPETaperStroke const * lpe = dynamic_cast (_effect); + return lpe->end_attach_point; +} } diff --git a/src/live_effects/lpe-taperstroke.h b/src/live_effects/lpe-taperstroke.h index 29a56f77c..997209543 100644 --- a/src/live_effects/lpe-taperstroke.h +++ b/src/live_effects/lpe-taperstroke.h @@ -20,39 +20,39 @@ namespace Inkscape { namespace LivePathEffect { namespace TpS { - // we need a separate namespace to avoid clashes with other LPEs - class KnotHolderEntityAttachBegin; - class KnotHolderEntityAttachEnd; +// we need a separate namespace to avoid clashes with other LPEs +class KnotHolderEntityAttachBegin; +class KnotHolderEntityAttachEnd; } class LPETaperStroke : public Effect { public: - LPETaperStroke(LivePathEffectObject *lpeobject); - virtual ~LPETaperStroke(); - - virtual void doOnApply(SPLPEItem const* lpeitem); - virtual void doOnRemove(SPLPEItem const* lpeitem); + LPETaperStroke(LivePathEffectObject *lpeobject); + virtual ~LPETaperStroke(); - virtual Geom::PathVector doEffect_path (Geom::PathVector const& path_in); - Geom::PathVector doEffect_simplePath(Geom::PathVector const& path_in); - - virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item); + virtual void doOnApply(SPLPEItem const* lpeitem); + virtual void doOnRemove(SPLPEItem const* lpeitem); - friend class TpS::KnotHolderEntityAttachBegin; - friend class TpS::KnotHolderEntityAttachEnd; + virtual Geom::PathVector doEffect_path (Geom::PathVector const& path_in); + Geom::PathVector doEffect_simplePath(Geom::PathVector const& path_in); + + virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item); + + friend class TpS::KnotHolderEntityAttachBegin; + friend class TpS::KnotHolderEntityAttachEnd; private: - ScalarParam line_width; - ScalarParam attach_start; - ScalarParam attach_end; - ScalarParam smoothing; - EnumParam join_type; - ScalarParam miter_limit; + ScalarParam line_width; + ScalarParam attach_start; + ScalarParam attach_end; + ScalarParam smoothing; + EnumParam join_type; + ScalarParam miter_limit; - Geom::Point start_attach_point; - Geom::Point end_attach_point; + Geom::Point start_attach_point; + Geom::Point end_attach_point; - LPETaperStroke(const LPETaperStroke&); - LPETaperStroke& operator=(const LPETaperStroke&); + LPETaperStroke(const LPETaperStroke&); + LPETaperStroke& operator=(const LPETaperStroke&); }; } //namespace LivePathEffect diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp old mode 100755 new mode 100644 index 6a47285a0..ad39a7416 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -9,133 +9,128 @@ #include <2geom/path-sink.h> #include -namespace Geom +namespace Geom { +/** +* Refer to: Weisstein, Eric W. "Circle-Circle Intersection." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/Circle-CircleIntersection.html +* +* @return 0 if no intersection +* @return 1 if one circle is contained in the other +* @return 2 if intersections are found (they are written to p0 and p1) +*/ +static int circle_circle_intersection(Circle const &circle0, Circle const &circle1, + Point & p0, Point & p1) +{ + Point X0 = circle0.center(); + double r0 = circle0.ray(); + Point X1 = circle1.center(); + double r1 = circle1.ray(); + + /* dx and dy are the vertical and horizontal distances between + * the circle centers. + */ + Point D = X1 - X0; + + /* Determine the straight-line distance between the centers. */ + double d = L2(D); + + /* Check for solvability. */ + if (d > (r0 + r1)) { + /* no solution. circles do not intersect. */ + return 0; + } + if (d <= fabs(r0 - r1)) { + /* no solution. one circle is contained in the other */ + return 1; + } + + /* 'point 2' is the point where the line through the circle + * intersection points crosses the line between the circle + * centers. + */ + + /* Determine the distance from point 0 to point 2. */ + double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; + + /* Determine the coordinates of point 2. */ + Point p2 = X0 + D * (a/d); + + /* Determine the distance from point 2 to either of the + * intersection points. + */ + double h = std::sqrt((r0*r0) - (a*a)); + + /* Now determine the offsets of the intersection points from + * point 2. + */ + Point r = (h/d)*rot90(D); + + /* Determine the absolute intersection points. */ + p0 = p2 + r; + p1 = p2 - r; + + return 2; +} +/** +* Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t. +* Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt). +*/ +static Circle touching_circle( D2 const &curve, double t, double tol=0.01 ) { - /** - * Refer to: Weisstein, Eric W. "Circle-Circle Intersection." - From MathWorld--A Wolfram Web Resource. - http://mathworld.wolfram.com/Circle-CircleIntersection.html - * - * @return 0 if no intersection - * @return 1 if one circle is contained in the other - * @return 2 if intersections are found (they are written to p0 and p1) - */ - static int circle_circle_intersection(Circle const &circle0, Circle const &circle1, - Point & p0, Point & p1) - { - Point X0 = circle0.center(); - double r0 = circle0.ray(); - Point X1 = circle1.center(); - double r1 = circle1.ray(); - - /* dx and dy are the vertical and horizontal distances between - * the circle centers. - */ - Point D = X1 - X0; - - /* Determine the straight-line distance between the centers. */ - double d = L2(D); - - /* Check for solvability. */ - if (d > (r0 + r1)) - { - /* no solution. circles do not intersect. */ - return 0; - } - if (d <= fabs(r0 - r1)) - { - /* no solution. one circle is contained in the other */ - return 1; - } - - /* 'point 2' is the point where the line through the circle - * intersection points crosses the line between the circle - * centers. - */ - - /* Determine the distance from point 0 to point 2. */ - double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; - - /* Determine the coordinates of point 2. */ - Point p2 = X0 + D * (a/d); - - /* Determine the distance from point 2 to either of the - * intersection points. - */ - double h = std::sqrt((r0*r0) - (a*a)); - - /* Now determine the offsets of the intersection points from - * point 2. - */ - Point r = (h/d)*rot90(D); - - /* Determine the absolute intersection points. */ - p0 = p2 + r; - p1 = p2 - r; - - return 2; - } - /** - * Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t. - * Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt). - */ - static Circle touching_circle( D2 const &curve, double t, double tol=0.01 ) - { - D2 dM=derivative(curve); - if ( are_near(L2sq(dM(t)),0.) ) { - dM=derivative(dM); - } - if ( are_near(L2sq(dM(t)),0.) ) { // try second time - dM=derivative(dM); - } - Piecewise > unitv = unitVector(dM,tol); - Piecewise dMlength = dot(Piecewise >(dM),unitv); - Piecewise k = cross(derivative(unitv),unitv); - k = divide(k,dMlength,tol,3); - double curv = k(t); // note that this value is signed - - Geom::Point normal = unitTangentAt(curve, t).cw(); - double radius = 1/curv; - Geom::Point center = curve(t) + radius*normal; - return Geom::Circle(center, fabs(radius)); - } - - static std::vector split_at_cusps(const Geom::Path& in) - { - Geom::PathVector out = Geom::PathVector(); - Geom::Path temp = Geom::Path(); - - for (unsigned path_descr = 0; path_descr < in.size(); path_descr++) - { - temp = Geom::Path(); - temp.append(in[path_descr]); - out.push_back(temp); - } - - return out; - } - - static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in) - { - std::vector temp; - sbasis_to_bezier(temp, sbasis_in, 4); - return Geom::CubicBezier( temp ); - } - - static boost::optional intersection_point( Geom::Point const & origin_a, Geom::Point const & vector_a, - Geom::Point const & origin_b, Geom::Point const & vector_b) - { - Geom::Coord denom = cross(vector_b, vector_a); - if (!Geom::are_near(denom,0.)){ - Geom::Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; - return origin_a + t * vector_a; - } - return boost::none; - } + D2 dM=derivative(curve); + if ( are_near(L2sq(dM(t)),0.) ) { + dM=derivative(dM); + } + if ( are_near(L2sq(dM(t)),0.) ) { // try second time + dM=derivative(dM); + } + Piecewise > unitv = unitVector(dM,tol); + Piecewise dMlength = dot(Piecewise >(dM),unitv); + Piecewise k = cross(derivative(unitv),unitv); + k = divide(k,dMlength,tol,3); + double curv = k(t); // note that this value is signed + + Geom::Point normal = unitTangentAt(curve, t).cw(); + double radius = 1/curv; + Geom::Point center = curve(t) + radius*normal; + return Geom::Circle(center, fabs(radius)); } -namespace Outline +static std::vector split_at_cusps(const Geom::Path& in) { + Geom::PathVector out = Geom::PathVector(); + Geom::Path temp = Geom::Path(); + + for (unsigned path_descr = 0; path_descr < in.size(); path_descr++) { + temp = Geom::Path(); + temp.append(in[path_descr]); + out.push_back(temp); + } + + return out; +} + +static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in) +{ + std::vector temp; + sbasis_to_bezier(temp, sbasis_in, 4); + return Geom::CubicBezier( temp ); +} + +static boost::optional intersection_point( Geom::Point const & origin_a, Geom::Point const & vector_a, + Geom::Point const & origin_b, Geom::Point const & vector_b) +{ + Geom::Coord denom = cross(vector_b, vector_a); + if (!Geom::are_near(denom,0.)) { + Geom::Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; + return origin_a + t * vector_a; + } + return boost::none; +} +} + +namespace Outline { typedef Geom::D2 D2SB; typedef Geom::Piecewise PWD2; @@ -160,72 +155,70 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) unsigned order = bezierOrder(&cbc1); Geom::Point start_point; - Geom::Point cross_point = cbc1.finalPoint(); - Geom::Point end_point; - - //assert(cbc1.finalPoint() == cbc2.initialPoint()); - //short circuiting? - if (cbc1.finalPoint() != cbc2.initialPoint()) { - printf("There was an issue when asserting that one curve's end is the start of the other. Line %d, File %s\n" - "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); - return false; + Geom::Point cross_point = cbc1.finalPoint(); + Geom::Point end_point; + + //assert(cbc1.finalPoint() == cbc2.initialPoint()); + //short circuiting? + if (cbc1.finalPoint() != cbc2.initialPoint()) { + printf("There was an issue when asserting that one curve's end is the start of the other. Line %d, File %s\n" + "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); + return false; + } + switch (order) { + case 3: + start_point = (static_cast(&cbc1))->operator[] (2); + //major league b***f***ing + if (are_near(start_point, cross_point, 0.0000001)) { + start_point = (static_cast(&cbc1))->operator[] (1); } - switch (order) - { - case 3: - start_point = (static_cast(&cbc1))->operator[] (2); - //major league b***f***ing - if (are_near(start_point, cross_point, 0.0000001)) { - start_point = (static_cast(&cbc1))->operator[] (1); - } - break; - case 2: - //this never happens - start_point = (static_cast(&cbc1))->operator[] (1); - break; - case 1: - default: - start_point = cbc1.initialPoint(); - } - - order = Outline::bezierOrder(&cbc2); - - switch (order) - { - case 3: - end_point = (static_cast(&cbc2))->operator[] (1); - if (are_near(end_point, cross_point, 0.0000001)) { - end_point = (static_cast(&cbc2))->operator[] (2); - } - break; - case 2: - end_point = (static_cast(&cbc2))->operator[] (1); - break; - case 1: - default: - end_point = cbc2.finalPoint(); - } - //got our three points, now let's see what their clockwise angle is - - //Much credit to Wikipedia for the following ( http://en.wikipedia.org/wiki/Graham_scan ) - /******************************************************************** + break; + case 2: + //this never happens + start_point = (static_cast(&cbc1))->operator[] (1); + break; + case 1: + default: + start_point = cbc1.initialPoint(); + } + + order = Outline::bezierOrder(&cbc2); + + switch (order) { + case 3: + end_point = (static_cast(&cbc2))->operator[] (1); + if (are_near(end_point, cross_point, 0.0000001)) { + end_point = (static_cast(&cbc2))->operator[] (2); + } + break; + case 2: + end_point = (static_cast(&cbc2))->operator[] (1); + break; + case 1: + default: + end_point = cbc2.finalPoint(); + } + //got our three points, now let's see what their clockwise angle is + + //Much credit to Wikipedia for the following ( http://en.wikipedia.org/wiki/Graham_scan ) + /******************************************************************** # Three points are a counter-clockwise turn if ccw > 0, clockwise if # ccw < 0, and collinear if ccw = 0 because ccw is a determinant that # gives the signed area of the triangle formed by p1, p2 and p3. function ccw(p1, p2, p3): - return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x) + return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x) *********************************************************************/ - + double ccw = ( (cross_point.x() - start_point.x()) * (end_point.y() - start_point.y()) ) - ( (cross_point.y() - start_point.y()) * (end_point.x() - start_point.x()) ); if (ccw > 0) return true; return false; } -void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, +void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, double line_width, bool outside = false) { - bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); + bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); if ( outside && !lineProblem ) { Geom::Path pth; pth.append(*cbc1); @@ -271,9 +264,8 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } else { boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) - { + cbc2->initialPoint(), tang2); + if (p) { //check size of miter Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; Geom::Coord len = distance(*p, point_on_path); @@ -284,19 +276,18 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } path_builder.appendNew (endPt); } - } - if ( outside && lineProblem ) { - Geom::Path pth; + } + if ( outside && lineProblem ) { + Geom::Path pth; pth.append(*cbc1); - Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); + Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); pth = Geom::Path(); - pth.append( *cbc2 ); - Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); - - boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) - { + pth.append( *cbc2 ); + Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); + + boost::optional p = intersection_point (cbc1->finalPoint(), tang1, + cbc2->initialPoint(), tang2); + if (p) { //check size of miter Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; Geom::Coord len = distance(*p, point_on_path); @@ -306,13 +297,13 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } path_builder.appendNew (endPt); - } - if ( !outside ) { - path_builder.appendNew (endPt); - } + } + if ( !outside ) { + path_builder.appendNew (endPt); + } } -void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, +void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, double line_width, bool outside = false) { //the most important work for the reflected join is done here @@ -320,7 +311,7 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb //determine where we are in the path. If we're on the inside, ignore //and just lineTo. On the outside, we'll do a little reflection magic :) Geom::Crossings cross; - + if (outside) { Geom::Path pth; pth.append(*cbc1); @@ -347,9 +338,8 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb if ( cross.empty() ) { //curves didn't cross; default to miter boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) - { + cbc2->initialPoint(), tang2); + if (p) { //check size of miter Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; Geom::Coord len = distance(*p, point_on_path); @@ -388,27 +378,26 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double // NOTE: it is important to notice the distinction between a Geom::Path and a livarot Path here! // if you do not see "Geom::" there is a different function set! Geom::PathVector pv = split_at_cusps(path_in); - + Path to_outline; Path outlined_result; - + Geom::Path path_builder = Geom::Path(); //the path to store the result in Geom::PathVector * path_vec; //needed because livarot returns a goddamn pointer - + const unsigned k = path_in.size(); - - for (unsigned u = 0; u < k; u+=2) - { + + for (unsigned u = 0; u < k; u+=2) { to_outline = Path(); outlined_result = Path(); - + to_outline.LoadPath(pv[u], Geom::Affine(), false, false); to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); //now a curve has been outside outlined and loaded into outlined_result - + //get the Geom::Path path_vec = outlined_result.MakePathVector(); - + //thing to do on the first run through if (u == 0) { //I could use the pv->operator[] (0) notation but that looks terrible @@ -417,58 +406,64 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double //get the curves ready for the operation Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); - + //do the reflection/extrapolation: - if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } - else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } + if (extrapolate) { + extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); + } else { + reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); + } } - + path_builder.append( (*path_vec)[0] ); - + //outline the next segment, but don't store it yet if (path_vec) delete path_vec; - + if (u < k - 1) { outlined_result = Path(); to_outline = Path(); - + to_outline.LoadPath(pv[u+1], Geom::Affine(), false, false); to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); - + path_vec = outlined_result.MakePathVector(); - + //get the curves ready for the operation Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); - + //do the reflection/extrapolation: - if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); } - else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); } - + if (extrapolate) { + extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); + } else { + reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); + } + //Now we can store it. path_builder.append( (*path_vec)[0] ); - + if (cbc1) delete cbc1; if (cbc2) delete cbc2; if (path_vec) delete path_vec; } } - + return path_builder; } -Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, +Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtType butt, double miter_lim, bool extrapolate) { Geom::PathVector path_out; - + unsigned pv_size = path_in.size(); for (unsigned i = 0; i < pv_size; i++) { - + if (path_in[i].size() > 1) { //since you've made it this far, hopefully all this is obvious :P Geom::Path with_direction; @@ -484,8 +479,8 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, newPath = Geom::path_from_piecewise(pwd2, 0.01)[0]; //fuk this with_direction = Outline::doAdvHalfOutline( newPath, -line_width, miter_lim, extrapolate ); - against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); - + against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); + /*if (dynamic_cast *>(&newPath[newPath.size()])) { //delete the 'Z' newPath.erase_last(); @@ -497,54 +492,54 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, newPath.erase_last(); newPath.append(path_in[i][path_in[i].size() - 1]); newPath.appendNew(newPath.initialPoint()); - newPath.erase_last(); + newPath.erase_last(); }*/ } Geom::PathBuilder pb; - + //add in the...do I really need to say this? pb.moveTo(with_direction.initialPoint()); pb.append(with_direction); - + //add in our line caps if (!path_in[i].closed()) { switch (butt) { - case butt_straight: - pb.lineTo(against_direction.initialPoint()); - break; - case butt_round: - pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() ); - break; - case butt_pointy: - //I have ZERO idea what to do here. - pb.lineTo(against_direction.initialPoint()); - break; - case butt_square: - pb.lineTo(against_direction.initialPoint()); - break; + case butt_straight: + pb.lineTo(against_direction.initialPoint()); + break; + case butt_round: + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() ); + break; + case butt_pointy: + //I have ZERO idea what to do here. + pb.lineTo(against_direction.initialPoint()); + break; + case butt_square: + pb.lineTo(against_direction.initialPoint()); + break; } } else { pb.moveTo(against_direction.initialPoint()); } - + pb.append(against_direction); - + //cap (if necessary) if (!path_in[i].closed()) { switch (butt) { - case butt_straight: - pb.lineTo(with_direction.initialPoint()); - break; - case butt_round: - pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() ); - break; - case butt_pointy: - //I have ZERO idea what to do here. - pb.lineTo(with_direction.initialPoint()); - break; - case butt_square: - pb.lineTo(with_direction.initialPoint()); - break; + case butt_straight: + pb.lineTo(with_direction.initialPoint()); + break; + case butt_round: + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() ); + break; + case butt_pointy: + //I have ZERO idea what to do here. + pb.lineTo(with_direction.initialPoint()); + break; + case butt_square: + pb.lineTo(with_direction.initialPoint()); + break; } } pb.flush(); @@ -554,7 +549,7 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, } else { Path p = Path(); Path outlinepath = Path(); - + p.LoadPath(path_in[i], Geom::Affine(), false, false); p.Outline(&outlinepath, line_width / 2, static_cast(join), butt, miter_lim); Geom::PathVector *pv_p = outlinepath.MakePathVector(); diff --git a/src/live_effects/pathoutlineprovider.h b/src/live_effects/pathoutlineprovider.h index 23cc7e2c4..27bc62d45 100644 --- a/src/live_effects/pathoutlineprovider.h +++ b/src/live_effects/pathoutlineprovider.h @@ -12,15 +12,15 @@ enum LineJoinType { LINEJOIN_EXTRAPOLATED }; -namespace Outline +namespace Outline { - unsigned bezierOrder (const Geom::Curve* curve_in); - std::vector PathVectorOutline(std::vector const & path_in, double line_width, ButtType linecap_type, - LineJoinType linejoin_type, double miter_limit); - - Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, - ButtType butt, double miter_lim, bool extrapolate = false); - Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit); + unsigned bezierOrder (const Geom::Curve* curve_in); + std::vector PathVectorOutline(std::vector const & path_in, double line_width, ButtType linecap_type, + LineJoinType linejoin_type, double miter_limit); + + /*Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, + ButtType butt, double miter_lim, bool extrapolate = false);*/ + Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit); } #endif // _SEEN_PATH_OUTLINE_H -- cgit v1.2.3