diff options
| -rw-r--r-- | src/helper/geom-pathstroke.cpp | 26 | ||||
| -rw-r--r-- | src/helper/geom-pathstroke.h | 2 | ||||
| -rw-r--r-- | src/live_effects/lpe-offset.cpp | 286 | ||||
| -rw-r--r-- | src/live_effects/lpe-offset.h | 3 |
4 files changed, 150 insertions, 167 deletions
diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index 7e4599beb..ead8bfb3b 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -1033,7 +1033,6 @@ Geom::PathVector outline( Geom::PathBuilder res; Geom::Path with_dir = half_outline(input, width/2., miter, join, tolerance); Geom::Path against_dir = half_outline(input.reversed(), width/2., miter, join, tolerance); - res.moveTo(with_dir[0].initialPoint()); res.append(with_dir); @@ -1103,6 +1102,20 @@ Geom::Path half_outline( const Geom::Curve &closingline = input.back_closed(); const size_t k = (are_near(closingline.initialPoint(), closingline.finalPoint()) && input.closed() ) ?input.size_open():input.size_default(); + + size_t outside = 0; + for (size_t u = 0; u < k; u += 2) { + if (u != 0) { + tangents(tang, input[u-1], input[u]); + if (Geom::cross(tang[0], tang[1]) > 0) { + outside --; + } else { + outside ++; + } + } + } + bool on_outside = outside >= 0; + for (size_t u = 0; u < k; u += 2) { temp.clear(); @@ -1113,7 +1126,7 @@ Geom::Path half_outline( res.append(temp); } else { tangents(tang, input[u-1], input[u]); - outline_join(res, temp, tang[0], tang[1], width, miter, join); + outline_join(res, temp, tang[0], tang[1], width, miter, on_outside, join); } // odd number of paths @@ -1121,7 +1134,7 @@ Geom::Path half_outline( temp.clear(); offset_curve(temp, &input[u+1], width, tolerance); tangents(tang, input[u], input[u+1]); - outline_join(res, temp, tang[0], tang[1], width, miter, join); + outline_join(res, temp, tang[0], tang[1], width, miter, on_outside, join); } } if (input.closed()) { @@ -1132,17 +1145,16 @@ Geom::Path half_outline( Geom::Path temp2; temp2.append(c2); tangents(tang, input.back(), input.front()); - outline_join(temp, temp2, tang[0], tang[1], width, miter, join); + outline_join(temp, temp2, tang[0], tang[1], width, miter, on_outside, join); res.erase(res.begin()); res.erase_last(); res.append(temp); res.close(); } - return res; } -void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, Inkscape::LineJoinType join) +void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, bool on_outside, Inkscape::LineJoinType join) { if (res.size() == 0 || temp.size() == 0) return; @@ -1156,8 +1168,6 @@ void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, join_data jd(res, temp, in_tang, out_tang, miter, width); - bool on_outside = (Geom::cross(in_tang, out_tang) > 0); - if (on_outside) { join_func *jf; switch (join) { diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h index 73d35b402..8224b59b8 100644 --- a/src/helper/geom-pathstroke.h +++ b/src/helper/geom-pathstroke.h @@ -91,7 +91,7 @@ Geom::Path half_outline( * @param[in] miter * @param[in] join */ -void outline_join(Geom::Path &res, Geom::Path const& outgoing, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, LineJoinType join); +void outline_join(Geom::Path &res, Geom::Path const& outgoing, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, bool on_outside, LineJoinType join); } // namespace Inkscape diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp index 447534f73..da86ce62d 100644 --- a/src/live_effects/lpe-offset.cpp +++ b/src/live_effects/lpe-offset.cpp @@ -48,13 +48,11 @@ namespace LivePathEffect { namespace OfS { class KnotHolderEntityOffsetPoint : public LPEKnotHolderEntity { public: - KnotHolderEntityOffsetPoint(LPEOffset * effect) : LPEKnotHolderEntity(effect) {inset = false; previous = Geom::Point(Geom::infinity(),Geom::infinity());} + KnotHolderEntityOffsetPoint(LPEOffset * effect) : LPEKnotHolderEntity(effect) {} void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override; - void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override {}; + void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override; Geom::Point knot_get() const override; private: - bool inset; - Geom::Point previous; }; } // OfS @@ -127,43 +125,27 @@ sp_flatten(Geom::PathVector &pathvector, bool evenodd) pathvector = sp_svg_read_pathv(res_d); } -Geom::PathVector -sp_get_one(Geom::PathVector pathvector, bool outer) +double +LPEOffset::sp_get_offset(Geom::Point &origin) { - Geom::OptRect bbox; - Geom::Path ret; - Geom::PathVector ret_pv; - if (pathvector.size() == 0) { - return ret_pv; - } - Geom::Path path0 = pathvector.at(0); - if (pathvector.size() == 1) { - ret_pv.push_back(path0); - return ret_pv; - } - Geom::Path path1 = pathvector.at(1); - bool push = false; - if (path0.boundsFast()->contains(path1.boundsFast())) { - push = true; - } - if (!outer) { - push = !push; + int winding_value = filled_rule_pathv.winding(origin); + bool inset = false; + if (winding_value % 2 != 0) { + inset = true; } - ret_pv.push_back(pathvector.at(push?0:1)); - return ret_pv; -} - -static void -sp_set_origin(Geom::PathVector original_pathv, Geom::Point &origin) -{ - double size = 0; - Geom::PathVector bigger = sp_get_one(original_pathv, true); - boost::optional< Geom::PathVectorTime > pathvectortime = bigger.nearestTime(origin); + double offset = 0; + boost::optional< Geom::PathVectorTime > pathvectortime = filled_rule_pathv.nearestTime(origin); + Geom::Point nearest = origin; + double distance = 0; if (pathvectortime) { Geom::PathTime pathtime = pathvectortime->asPathTime(); - origin = bigger[(*pathvectortime).path_index].pointAt(pathtime.curve_index + pathtime.t); + nearest = filled_rule_pathv[(*pathvectortime).path_index].pointAt(pathtime.curve_index + pathtime.t); } - + offset = Geom::distance(origin, nearest); + if (inset) { + offset *= -1; + } + return offset; } void @@ -206,9 +188,21 @@ LPEOffset::doBeforeEffect (SPLPEItem const* lpeitem) evenodd = true; } original_pathv = pathv_to_linear_and_cubic_beziers(pathvector_before_effect); - sp_flatten(original_pathv, evenodd); + filled_rule_pathv = original_pathv; + sp_flatten(filled_rule_pathv, evenodd); origin = offset_pt; - sp_set_origin(original_pathv, origin); +} + +int offset_winding(Geom::PathVector pathvector, Geom::Path path) +{ + int wind = 0; + Geom::Point p = path.initialPoint(); + for (auto i:pathvector) { + if (i == path) continue; + if (!i.boundsFast().contains(p)) continue; + wind += i.winding(p); + } + return wind; } Geom::PathVector @@ -216,135 +210,120 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) { if (offset == 0.0) { offset_pt = original_pathv[0].initialPoint(); - origin = offset_pt; return original_pathv; } + Geom::PathVector work; Geom::PathVector ret; - Geom::PathVector ret_painter; - Geom::PathVector ret_eraser; + Geom::PathVector ret_outline; + Geom::PathIntersectionGraph *pig; for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { - Geom::PathVector ret_painter_tmp; - Geom::PathVector ret_eraser_tmp; + Geom::Path original = (*path_it); + if (original.empty()) { + continue; + } + bool added = false; + for (auto cross:Geom::self_crossings(original)) { + if (cross.ta != cross.tb) { + Geom::PathVector tmp; + tmp.push_back(original); + sp_flatten(tmp, false); + work.insert(work.begin(), tmp.begin(), tmp.end()); + added = true; + break; + } + } + if (!added) { + work.push_back(original); + } + } + for (Geom::PathVector::const_iterator path_it = work.begin(); path_it != work.end(); ++path_it) { Geom::Path original = (*path_it); if (original.empty()) { continue; } - Geom::PathVector original_pv; - original_pv.push_back(original); - Geom::OptRect bounds = original_pv.boundsFast(); + int wdg = offset_winding(work, original); + bool path_inside = wdg % 2 != 0; + double gap = path_inside ? 0.05 :-0.05; + Geom::PathVector outline_gap = Inkscape::outline(original, std::abs(offset) * 2 + gap , + (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit), + static_cast<LineJoinType>(linejoin_type.get_value()), + static_cast<LineCapType>(BUTT_FLAT)); Geom::PathVector outline = Inkscape::outline(original, std::abs(offset) * 2 , (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit), static_cast<LineJoinType>(linejoin_type.get_value()), static_cast<LineCapType>(BUTT_FLAT)); - sp_flatten(outline, false); - if (outline.empty()) { + if (outline.size() < 2) { continue; } - Geom::Point winding_point = Geom::Point(Geom::infinity(), Geom::infinity()); - int wind = 0; - double dist = Geom::infinity(); - pathv_matrix_point_bbox_wind_distance(original_pathv, Geom::identity(), winding_point, nullptr, &wind, &dist, 0.5, nullptr); - bool path_inside = wind % 2 != 0; - if (offset > 0 && !path_inside) { - ret_painter_tmp = sp_get_one(outline, true); - } else if (offset < 0 && !path_inside) { - ret_painter_tmp = sp_get_one(outline, false); - } else if (offset > 0 && path_inside) { - Geom::OptRect boundsOutline = outline.boundsFast(); - if(!boundsOutline || - !bounds || - ((*boundsOutline).width()+(*boundsOutline).height())/2.0 > (*bounds).width() + (*bounds).height()){ - continue; - } - ret_eraser_tmp = sp_get_one(outline, false); - } else /*if (offset < 0 && path_inside) */ { - ret_eraser_tmp = sp_get_one(outline, true); - } - ret_painter.insert(ret_painter.end(),ret_painter_tmp.begin(),ret_painter_tmp.end()); - ret_eraser.insert(ret_eraser.end(),ret_eraser_tmp.begin(),ret_eraser_tmp.end()); - } - - Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(ret_eraser, ret_painter); - if (pig && !ret_painter.empty() && !ret_eraser.empty()) { - ret = pig->getBminusA(); - } else { - ret = ret_painter; - } - Geom::PathVector re_painter; - for (Geom::PathVector::const_iterator eraser_it = ret_eraser.begin(); eraser_it != ret_eraser.end(); ++eraser_it) { - Geom::Path eraser = (*eraser_it); - if (eraser.empty()) { - continue; + Geom::PathVector big_gap_ret; + Geom::PathVector big; + big.push_back(outline[0]); + Geom::PathVector big_gap; + big_gap.push_back(outline_gap[0]); + Geom::PathVector small; + small.insert(small.end(), outline.begin() + 1, outline.end()); + Geom::PathVector small_gap; + small_gap.insert(small_gap.end(), outline_gap.begin() + 1, outline_gap.end()); + Geom::OptRect big_bounds = big.boundsFast(); + Geom::OptRect small_bounds = small.boundsFast(); + bool reversed = small_bounds.contains(big_bounds); + if (reversed) { + big.clear(); + big.insert(big.end(), outline.begin() + 1, outline.end()); + big_gap.clear(); + big_gap.insert(big_gap.end(), outline_gap.begin() + 1, outline_gap.end()); + small.clear(); + small.push_back(outline[0]); + small_gap.clear(); + small_gap.push_back(outline[0]); } - Geom::OptRect eraser_bbox = eraser.boundsFast(); - for (Geom::PathVector::iterator painter_it = ret_painter.begin(); painter_it != ret_painter.end();) { - Geom::Path painter = (*painter_it); - if (painter.empty()) { - painter_it = ret_painter.erase(painter_it); - continue; + big_bounds = big.boundsFast(); + small_bounds = small.boundsFast(); + Geom::OptRect big_gap_bounds = big.boundsFast(); + Geom::OptRect small_gap_bounds = small.boundsFast(); + Geom::OptRect original_bounds = original.boundsFast(); + if (offset < 0) { + if (((*original_bounds).width() + (*original_bounds).height()) / 2.0 > std::abs(offset) * 2) { + big_gap_ret.insert(big_gap_ret.end(), big_gap.begin(), big_gap.end()); } - Geom::OptRect painter_bbox = painter.boundsFast(); - if ((eraser_bbox.intersects(painter_bbox) && - !painter_bbox.contains(eraser_bbox) ) || - eraser_bbox.contains(painter_bbox) ) - { - re_painter.push_back(painter); - painter_it = ret_painter.erase(painter_it); + } else { + if (path_inside) { + if (((*original_bounds).width() + (*original_bounds).height()) / 2.0 > offset * 2) { + big_gap_ret.insert(big_gap_ret.end(),small_gap.begin(), small_gap.end()); + } } else { - ++painter_it; - } + big_gap_ret.insert(big_gap_ret.end(), big_gap.begin(), big_gap.end()); + } } - } - Geom::PathIntersectionGraph *pig_b = new Geom::PathIntersectionGraph(ret, re_painter); - if (pig_b && !ret.empty() && !re_painter.empty()) { - ret = pig_b->getUnion(); - } - return ret; -} -Gtk::Widget *LPEOffset::newWidget() -{ - // use manage here, because after deletion of Effect object, others might still be pointing to this widget. - Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); - - vbox->set_border_width(5); - vbox->set_homogeneous(false); - vbox->set_spacing(6); - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); - std::vector<Parameter *>::iterator it = param_vector.begin(); - while (it != param_vector.end()) { - if ((*it)->widget_is_visible) { - Parameter * param = *it; - Gtk::Widget * widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); - Glib::ustring * tip = param->param_getTooltip(); - if (widg) { - if (param->param_key == "offset") { - offset_widget = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg)); - widg = dynamic_cast<Gtk::Widget *>(offset_widget); - if (widg) { - Gtk::HBox * hbox_weight_steps = dynamic_cast<Gtk::HBox *>(widg); - std::vector< Gtk::Widget* > childList = hbox_weight_steps->get_children(); - Gtk::Entry* entry_widget = dynamic_cast<Gtk::Entry *>(childList[1]); - entry_widget->set_width_chars(9); - } - } - Glib::ustring *tip = param->param_getTooltip(); - if (widg) { - vbox->pack_start(*widg, true, true, 2); - if (tip) { - widg->set_tooltip_text(*tip); - } else { - widg->set_tooltip_text(""); - widg->set_has_tooltip(false); - } + if (path_inside) { + Geom::OptRect bounds; + Geom::Path tmp = outline[0]; + for (auto path:outline) { + Geom::OptRect path_bounds = path.boundsFast(); + if (path_bounds.contains(bounds)) { + tmp = path; } + bounds = path_bounds; } + outline.clear(); + outline.push_back(tmp); + } else { + sp_flatten(outline, false); } - - ++it; + ret.insert(ret.end(), big_gap_ret.begin(),big_gap_ret.end()); + ret_outline.insert(ret_outline.end(), outline.begin(), outline.end()); } - return dynamic_cast<Gtk::Widget *>(vbox); + sp_flatten(ret_outline, false); + if (offset < 0) { + pig = new Geom::PathIntersectionGraph(ret, ret_outline); + if (pig && !ret_outline.empty() && !ret.empty()) { + ret = pig->getAminusB(); + } + } + sp_flatten(ret, false); + return ret; } void @@ -373,28 +352,21 @@ void LPEOffset::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) namespace OfS { +void KnotHolderEntityOffsetPoint::knot_ungrabbed(Geom::Point const &p, Geom::Point const& /*origin*/, guint state) +{ + LPEOffset* lpe = dynamic_cast<LPEOffset *>(_effect); + lpe->upd_params = true; +} void KnotHolderEntityOffsetPoint::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state) { using namespace Geom; LPEOffset* lpe = dynamic_cast<LPEOffset *>(_effect); Geom::Point s = snap_knot_position(p, state); - previous = s; - inset = false; - int winding_value = lpe->original_pathv.winding(s); - if (winding_value % 2 != 0) { - inset = true; - } - + double offset = lpe->sp_get_offset(s); lpe->offset_pt = s; - double offset = 0; - Geom::Point origin = lpe->offset_pt; - sp_set_origin(lpe->original_pathv, origin); - offset = Geom::distance(origin, lpe->offset_pt); - if (inset) { - offset *= -1; - } lpe->offset.param_set_value(offset); + if (lpe->update_on_knot_move) { sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, false); } diff --git a/src/live_effects/lpe-offset.h b/src/live_effects/lpe-offset.h index 82fecbc93..bbd192860 100644 --- a/src/live_effects/lpe-offset.h +++ b/src/live_effects/lpe-offset.h @@ -36,10 +36,10 @@ public: void doBeforeEffect (SPLPEItem const* lpeitem) override; Geom::PathVector doEffect_path (Geom::PathVector const & path_in) override; void doOnApply(SPLPEItem const* lpeitem) override; - Gtk::Widget *newWidget() override; void calculateOffset (Geom::PathVector const & path_in); void drawHandle(Geom::Point p); void addKnotHolderEntities(KnotHolder * knotholder, SPItem * item) override; + double sp_get_offset(Geom::Point &origin); friend class OfS::KnotHolderEntityOffsetPoint; protected: void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) override; @@ -56,6 +56,7 @@ private: bool evenodd; KnotHolderEntity * _knot_entity; Geom::PathVector original_pathv; + Geom::PathVector filled_rule_pathv; Inkscape::UI::Widget::Scalar *offset_widget; LPEOffset(const LPEOffset&); |
