From b275ac2b456ece0331974a2d1c4f82e39d795364 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sat, 9 Sep 2023 18:05:24 +0200 Subject: [PATCH] remove extra holes on LPE offset --- src/live_effects/lpe-offset.cpp | 206 ++++---------------------------- 1 file changed, 24 insertions(+), 182 deletions(-) diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp index 704729d96d..efdcf933a4 100644 --- a/src/live_effects/lpe-offset.cpp +++ b/src/live_effects/lpe-offset.cpp @@ -164,21 +164,6 @@ Geom::Point get_nearest_point(Geom::PathVector pathv, Geom::Point point) return res; } -/* double get_separation(Geom::PathVector pathv, Geom::Point point, Geom::Point point_b) -{ - Geom::Point res = Geom::Point(Geom::infinity(), Geom::infinity()); - std::optional pathvectortime = pathv.nearestTime(point); - std::optional pathvectortime_b = pathv.nearestTime(point_b); - if (pathvectortime && pathvectortime_b) { - Geom::PathTime pathtime = pathvectortime->asPathTime(); - Geom::PathTime pathtime_b = pathvectortime_b->asPathTime(); - if ((*pathvectortime).path_index == (*pathvectortime_b).path_index) { - return std::abs((pathtime.curve_index + pathtime.t) - (pathtime_b.curve_index + pathtime_b.t)); - } - } - return -1; -} */ - void LPEOffset::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { refresh_widgets = true; @@ -278,159 +263,6 @@ void LPEOffset::doAfterEffect(SPLPEItem const * /*lpeitem*/, SPCurve *curve) } } -// TODO: find a way to not remove wanted self intersections -// previously are some failed attempts - -/* // Taken from Knot LPE duple code -static Geom::Path::size_type size_nondegenerate(Geom::Path const &path) -{ - Geom::Path::size_type retval = path.size_default(); - const Geom::Curve &closingline = path.back_closed(); - // the closing line segment is always of type - // Geom::LineSegment. - if (are_near(closingline.initialPoint(), closingline.finalPoint())) { - // closingline.isDegenerate() did not work, because it only checks for - // *exact* zero length, which goes wrong for relative coordinates and - // rounding errors... - // the closing line segment has zero-length. So stop before that one! - retval = path.size_open(); - } - return retval; -} -gint get_nearest_corner(Geom::OptRect bbox, Geom::Point point) -{ - if (bbox) { - double distance_a = Geom::distance(point, (*bbox).corner(0)); - double distance_b = Geom::distance(point, (*bbox).corner(1)); - double distance_c = Geom::distance(point, (*bbox).corner(2)); - double distance_d = Geom::distance(point, (*bbox).corner(3)); - std::vector distances{distance_a, distance_b, distance_c, distance_d}; - std::vector::iterator mindistance = std::min_element(distances.begin(), distances.end()); - return std::distance(distances.begin(), mindistance); - } - return -1; -} - -// This way not work with good selfintersections on consecutive curves -// and when there is nodes nearest to points -// I try different methods to cleanup without luky -// if anyone is interested there is a way to explore -// if in original path the section into the 2 nearest point dont have -// self intersections we can suppose this is a intersection to remove -// it works very well but in good selfintersections work only in one offset direction -bool consecutiveCurves(Geom::Path pathin, Geom::Point p) { - Geom::Coord mindist = std::numeric_limits::max(); - size_t pos; - for (size_t i = 0; i < pathin.size_default(); ++i) { - Geom::Curve const &c = pathin.at(i); - double dist = Geom::distance(p, c.boundsFast()); - if (dist >= mindist) { - continue; - } - Geom::Coord t = c.nearestTime(p); - Geom::Coord d = Geom::distance(c.pointAt(t), p); - if (d < mindist) { - pos = i; - mindist = d; - } - } - size_t pos_b; - mindist = std::numeric_limits::max(); - for (size_t i = 0; i < pathin.size_default(); ++i) { - Geom::Curve const &c = pathin.at(i); - double dist = Geom::distance(p, c.boundsFast()); - if (dist >= mindist || pos == i) { - continue; - } - Geom::Coord t = c.nearestTime(p); - Geom::Coord d = Geom::distance(c.pointAt(t), p); - if (d < mindist) { - pos_b = i; - mindist = d; - } - } - if (Geom::are_near(Geom::distance(pos,pos_b), 1)) { - return true; - } - return false; -} - -Geom::Path removeIntersects(Geom::Path pathin, Geom::Path pathorig, size_t skipcross) -{ - Geom::Path out; - Geom::Crossings crossings = Geom::self_crossings(pathin); - size_t counter = 0; - for (auto cross : crossings) { - if (!Geom::are_near(cross.ta, cross.tb, 0.01)) { - size_t sizepath = size_nondegenerate(pathin); - double ta = cross.ta > cross.tb ? cross.tb : cross.ta; - double tb = cross.ta > cross.tb ? cross.ta : cross.tb; - if (!pathin.closed()) { - counter++; - if (skipcross >= counter) { - continue; - } - bool removeint = consecutiveCurves(pathorig, pathin.pointAt(ta)); - Geom::Path p0 = pathin; - if (removeint) { - Geom::Path p1 = pathin.portion(ta, tb); - if ((*p1.boundsFast()).maxExtent() > 0.01) { - p0 = pathin.portion(0, ta); - if (!Geom::are_near(tb, sizepath, 0.01)) { - Geom::Path p2 = pathin.portion(tb, sizepath); - p0.setFinal(p2.initialPoint()); - p0.append(p2); - } - } else { - skipcross++; - } - } else { - skipcross++; - } - out = removeIntersects(p0, pathorig, skipcross); - return out; - } - } - } - return pathin; -} */ - -Geom::Path removeIntersects(Geom::Path pathin) -{ - // I have a pending ping to moazin for 1.2 to fix open paths offeset self intesections (Jabiertxof) - // For 1.1 I comment the code because simply slow a lot or crash sometimes and never work really well - /* Geom::Path out; - Geom::Crossings crossings = Geom::self_crossings(pathin); - static size_t maxiter = 0; - if (!maxiter) { - maxiter = crossings.size(); - } - for (auto cross : crossings) { - maxiter--; - if (!maxiter) { - return pathin; - } - if (!Geom::are_near(cross.ta, cross.tb, 0.01)) { - size_t sizepath = size_nondegenerate(pathin); - double ta = cross.ta > cross.tb ? cross.tb : cross.ta; - double tb = cross.ta > cross.tb ? cross.ta : cross.tb; - if (!pathin.closed()) { - Geom::Path p0 = pathin; - Geom::Path p1 = pathin.portion(ta, tb); - p0 = pathin.portion(0, ta); - if (!Geom::are_near(tb, sizepath, 0.01)) { - Geom::Path p2 = pathin.portion(tb, sizepath); - p0.setFinal(p2.initialPoint()); - p0.append(p2); - } - out = removeIntersects(p0); - return out; - } - } - } */ - return pathin; -} - static Geom::PathVector sp_simplify_pathvector(Geom::PathVector original_pathv, double threshold) { @@ -470,6 +302,7 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) double to_offset = Inkscape::Util::Quantity::convert(offset, unit.get_abbreviation(), "px") / this->scale; Geom::PathVector open_pathv; Geom::PathVector closed_pathv; + Geom::PathVector closed_pathv_tmp; Geom::PathVector mix_pathv; Geom::PathVector mix_pathv_workon; Geom::PathVector orig_pathv = pathv_to_linear_and_cubic_beziers(path_in); @@ -482,13 +315,27 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) i.close(true); } if (i.closed()) { - closed_pathv.push_back(i); + closed_pathv_tmp.push_back(i); } else { open_pathv.push_back(i); } } - sp_flatten(closed_pathv, fillrule); - + sp_flatten(closed_pathv_tmp, fillrule); + for (auto pathin : closed_pathv_tmp) { + int winding_value = closed_pathv.winding(pathin.pointAt(0)); + if (winding_value % 2) { + Geom::OptRect bbox = pathin.boundsFast(); + if (bbox) { + Geom::OptRect bboxorig = bbox; + (*bbox).expandBy((-1 * to_offset) / 2.0); + if ((bbox).hasZeroArea()) { + continue; + } + } + } + closed_pathv.push_back(pathin); + } + closed_pathv_tmp.clear(); // we flatten using original fill rule mix_pathv = open_pathv; for (auto path : closed_pathv) { @@ -513,10 +360,7 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) } } for (auto pathin : closed_pathv) { - // Geom::OptRect bbox = pathin.boundsFast(); - // if (pbbox && (*pbbox).minExtent() > to_offset) { mix_pathv_workon.push_back(pathin); - //} } mix_pathv_workon.insert(mix_pathv_workon.begin(), open_pathv.begin(), open_pathv.end()); @@ -541,10 +385,7 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) Geom::Path tmp = half_outline( i, to_offset, (attempt_force_join ? std::numeric_limits::max() : miter_limit), join, tolerance); if (tmp.closed()) { - Geom::OptRect pbbox = tmp.boundsFast(); - if (pbbox && (*pbbox).minExtent() > to_offset) { - ret_closed_tmp.push_back(tmp); - } + ret_closed_tmp.push_back(tmp); } else { Geom::Path tmp_b = half_outline(i.reversed(), to_offset, (attempt_force_join ? std::numeric_limits::max() : miter_limit), @@ -553,10 +394,11 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) Geom::PathVector switch_pv_b(tmp_b); double distance_b = Geom::distance(offset_pt, get_nearest_point(switch_pv_a, offset_pt)); double distance_a = Geom::distance(offset_pt, get_nearest_point(switch_pv_b, offset_pt)); + // TODO: implement handle open paths removeIntersects in commented code om rev 19cd261a6631cc4f2ff752d7b61c9c599cbab886 if (distance_b < distance_a) { - ret_open.push_back(removeIntersects(tmp)); + ret_open.push_back(tmp); } else { - ret_open.push_back(removeIntersects(tmp_b)); + ret_open.push_back(tmp_b); } } } @@ -588,9 +430,9 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) double distance_b = Geom::distance(offset_pt, get_nearest_point(switch_pv_a, offset_pt)); double distance_a = Geom::distance(offset_pt, get_nearest_point(switch_pv_b, offset_pt)); if (distance_b < distance_a) { - ret_open.push_back(removeIntersects(tmp)); + ret_open.push_back(tmp); } else { - ret_open.push_back(removeIntersects(tmp_b)); + ret_open.push_back(tmp_b); } } } -- GitLab