mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 06:19:00 +08:00
Fix Healing of SVG shape for overlapped contour points in thin "bay".
SVG from @LukasMatena
This commit is contained in:
parent
df256cc2bd
commit
70460ae790
@ -92,17 +92,17 @@ struct SpikeDesc
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bevel_size">Size of spike width after cut of the tip, has to be grater than 2.5</param>
|
/// <param name="bevel_size">Size of spike width after cut of the tip, has to be grater than 2.5</param>
|
||||||
/// <param name="pixel_spike_length">When spike has same or more pixels with width less than 1 pixel</param>
|
/// <param name="pixel_spike_length">When spike has same or more pixels with width less than 1 pixel</param>
|
||||||
SpikeDesc(double bevel_size, double pixel_spike_length = 6)
|
SpikeDesc(double bevel_size, double pixel_spike_length = 6):
|
||||||
{
|
|
||||||
// create min angle given by spike_length
|
// create min angle given by spike_length
|
||||||
// Use it as minimal height of 1 pixel base spike
|
// Use it as minimal height of 1 pixel base spike
|
||||||
double angle = 2. * atan2(pixel_spike_length, .5); // [rad]
|
cos_angle(std::fabs(std::cos(
|
||||||
cos_angle = std::fabs(cos(angle));
|
/*angle*/ 2. * std::atan2(pixel_spike_length, .5)
|
||||||
|
))),
|
||||||
|
|
||||||
// When remove spike this angle is set.
|
// When remove spike this angle is set.
|
||||||
// Value must be grater than min_angle
|
// Value must be grater than min_angle
|
||||||
half_bevel = bevel_size / 2;
|
half_bevel(bevel_size / 2)
|
||||||
}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
// return TRUE when remove point. It could create polygon with 2 points.
|
// return TRUE when remove point. It could create polygon with 2 points.
|
||||||
@ -120,6 +120,8 @@ void remove_spikes(ExPolygons &expolygons, const SpikeDesc &spike_desc);
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// spike ... very sharp corner - when not removed cause iteration of heal process
|
||||||
|
// index ... index of duplicit point in polygon
|
||||||
bool priv::remove_when_spike(Polygon &polygon, size_t index, const SpikeDesc &spike_desc) {
|
bool priv::remove_when_spike(Polygon &polygon, size_t index, const SpikeDesc &spike_desc) {
|
||||||
|
|
||||||
std::optional<Point> add;
|
std::optional<Point> add;
|
||||||
@ -205,7 +207,8 @@ bool priv::remove_when_spike(Polygon &polygon, size_t index, const SpikeDesc &sp
|
|||||||
}
|
}
|
||||||
|
|
||||||
void priv::remove_spikes_in_duplicates(ExPolygons &expolygons, const Points &duplicates) {
|
void priv::remove_spikes_in_duplicates(ExPolygons &expolygons, const Points &duplicates) {
|
||||||
|
if (duplicates.empty())
|
||||||
|
return;
|
||||||
auto check = [](Polygon &polygon, const Point &d) -> bool {
|
auto check = [](Polygon &polygon, const Point &d) -> bool {
|
||||||
double spike_bevel = 1 / SHAPE_SCALE;
|
double spike_bevel = 1 / SHAPE_SCALE;
|
||||||
double spike_length = 5.;
|
double spike_length = 5.;
|
||||||
@ -486,16 +489,20 @@ bool priv::remove_self_intersections(ExPolygons &shape, unsigned max_iteration)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExPolygons Emboss::heal_shape(const Polygons &shape)
|
ExPolygons Emboss::heal_polygons(const Polygons &shape, bool is_non_zero)
|
||||||
{
|
{
|
||||||
|
const double clean_distance = 1.415; // little grater than sqrt(2)
|
||||||
|
ClipperLib::PolyFillType fill_type = is_non_zero ?
|
||||||
|
ClipperLib::pftNonZero : ClipperLib::pftEvenOdd;
|
||||||
|
|
||||||
// When edit this code check that font 'ALIENATE.TTF' and glyph 'i' still work
|
// When edit this code check that font 'ALIENATE.TTF' and glyph 'i' still work
|
||||||
// fix of self intersections
|
// fix of self intersections
|
||||||
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/SimplifyPolygon.htm
|
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/SimplifyPolygon.htm
|
||||||
ClipperLib::Paths paths = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(shape), ClipperLib::pftNonZero);
|
ClipperLib::Paths paths = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(shape), fill_type);
|
||||||
const double clean_distance = 1.415; // little grater than sqrt(2)
|
|
||||||
ClipperLib::CleanPolygons(paths, clean_distance);
|
ClipperLib::CleanPolygons(paths, clean_distance);
|
||||||
Polygons polygons = to_polygons(paths);
|
Polygons polygons = to_polygons(paths);
|
||||||
polygons.erase(std::remove_if(polygons.begin(), polygons.end(), [](const Polygon &p) { return p.size() < 3; }), polygons.end());
|
polygons.erase(std::remove_if(polygons.begin(), polygons.end(),
|
||||||
|
[](const Polygon &p) { return p.size() < 3; }), polygons.end());
|
||||||
|
|
||||||
// Do not remove all duplicates but do it better way
|
// Do not remove all duplicates but do it better way
|
||||||
// Overlap all duplicit points by rectangle 3x3
|
// Overlap all duplicit points by rectangle 3x3
|
||||||
@ -508,12 +515,11 @@ ExPolygons Emboss::heal_shape(const Polygons &shape)
|
|||||||
polygons.push_back(rect_3x3);
|
polygons.push_back(rect_3x3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ExPolygons res = Slic3r::union_ex(polygons, fill_type);
|
||||||
|
|
||||||
// TrueTypeFonts use non zero winding number
|
|
||||||
// https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01
|
const unsigned int max_iteration = 10;
|
||||||
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
bool is_healed = heal_expolygons(res, max_iteration);
|
||||||
ExPolygons res = Slic3r::union_ex(polygons, ClipperLib::pftNonZero);
|
|
||||||
heal_shape(res);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,35 +533,33 @@ void priv::visualize_heal(const std::string &svg_filepath, const ExPolygons &exp
|
|||||||
svg.draw(expolygons);
|
svg.draw(expolygons);
|
||||||
|
|
||||||
Points duplicits = collect_duplicates(pts);
|
Points duplicits = collect_duplicates(pts);
|
||||||
svg.draw(duplicits, "black", 7 / SHAPE_SCALE);
|
int black_size = std::max(bb.size().x(), bb.size().y()) / 20;
|
||||||
|
svg.draw(duplicits, "black", black_size);
|
||||||
|
|
||||||
Pointfs intersections_f = intersection_points(expolygons);
|
Pointfs intersections_f = intersection_points(expolygons);
|
||||||
Points intersections;
|
Points intersections;
|
||||||
intersections.reserve(intersections_f.size());
|
intersections.reserve(intersections_f.size());
|
||||||
std::transform(intersections_f.begin(), intersections_f.end(), std::back_inserter(intersections),
|
std::transform(intersections_f.begin(), intersections_f.end(), std::back_inserter(intersections),
|
||||||
[](const Vec2d &p) { return p.cast<int>(); });
|
[](const Vec2d &p) { return p.cast<int>(); });
|
||||||
svg.draw(intersections, "red", 8 / SHAPE_SCALE);
|
svg.draw(intersections, "red", black_size * 1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
|
bool Emboss::heal_expolygons(ExPolygons &shape, unsigned max_iteration)
|
||||||
{
|
{
|
||||||
return priv::heal_dupl_inter(shape, max_iteration);
|
return priv::heal_dupl_inter(shape, max_iteration);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HEAL_WITH_CLOSING
|
#ifndef HEAL_WITH_CLOSING
|
||||||
bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
|
namespace {
|
||||||
{
|
|
||||||
if (shape.empty()) return true;
|
|
||||||
|
|
||||||
// create loop permanent memory
|
Points get_unique_intersections(const ExPolygons &shape)
|
||||||
Polygons holes;
|
{
|
||||||
Points intersections;
|
|
||||||
while (--max_iteration) {
|
|
||||||
remove_same_neighbor(shape);
|
|
||||||
Pointfs intersections_f = intersection_points(shape);
|
Pointfs intersections_f = intersection_points(shape);
|
||||||
|
Points intersections; // result
|
||||||
|
if (intersections_f.empty())
|
||||||
|
return intersections;
|
||||||
|
|
||||||
// convert intersections into Points
|
// convert intersections into Points
|
||||||
assert(intersections.empty());
|
|
||||||
intersections.reserve(intersections_f.size());
|
intersections.reserve(intersections_f.size());
|
||||||
std::transform(intersections_f.begin(), intersections_f.end(), std::back_inserter(intersections),
|
std::transform(intersections_f.begin(), intersections_f.end(), std::back_inserter(intersections),
|
||||||
[](const Vec2d &p) { return Point(std::floor(p.x()), std::floor(p.y())); });
|
[](const Vec2d &p) { return Point(std::floor(p.x()), std::floor(p.y())); });
|
||||||
@ -564,15 +568,70 @@ bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
|
|||||||
std::sort(intersections.begin(), intersections.end());
|
std::sort(intersections.begin(), intersections.end());
|
||||||
auto it = std::unique(intersections.begin(), intersections.end());
|
auto it = std::unique(intersections.begin(), intersections.end());
|
||||||
intersections.erase(it, intersections.end());
|
intersections.erase(it, intersections.end());
|
||||||
|
return intersections;
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygons get_holes_with_points(const Polygons &holes, const Points &points)
|
||||||
|
{
|
||||||
|
Polygons result;
|
||||||
|
for (const Slic3r::Polygon &hole : holes)
|
||||||
|
for (const Point &p : points)
|
||||||
|
for (const Point &h : hole)
|
||||||
|
if (p == h) {
|
||||||
|
result.push_back(hole);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fill holes which create duplicits or intersections
|
||||||
|
/// When healing hole creates trouble in shape again try to heal by an union instead of diff_ex
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="holes">Holes which was substracted from shape previous</param>
|
||||||
|
/// <param name="duplicates">Current duplicates in shape</param>
|
||||||
|
/// <param name="intersections">Current intersections in shape</param>
|
||||||
|
/// <param name="shape">Partialy healed shape[could be modified]</param>
|
||||||
|
/// <returns>True when modify shape otherwise False</returns>
|
||||||
|
bool fill_trouble_holes(const Polygons &holes, const Points &duplicates, const Points &intersections, ExPolygons &shape)
|
||||||
|
{
|
||||||
|
if (holes.empty())
|
||||||
|
return false;
|
||||||
|
if (duplicates.empty() && intersections.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Polygons fill = get_holes_with_points(holes, duplicates);
|
||||||
|
append(fill, get_holes_with_points(holes, intersections));
|
||||||
|
if (fill.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
shape = union_ex(shape, fill);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
|
||||||
|
{
|
||||||
|
if (shape.empty()) return true;
|
||||||
|
|
||||||
|
// create loop permanent memory
|
||||||
|
Polygons holes;
|
||||||
|
while (--max_iteration) {
|
||||||
|
remove_same_neighbor(shape);
|
||||||
Points duplicates = collect_duplicates(to_points(shape));
|
Points duplicates = collect_duplicates(to_points(shape));
|
||||||
// duplicates are already uniqua and sorted
|
Points intersections = get_unique_intersections(shape);
|
||||||
|
|
||||||
// Check whether shape is already healed
|
// Check whether shape is already healed
|
||||||
if (intersections.empty() && duplicates.empty())
|
if (intersections.empty() && duplicates.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
assert(holes.empty());
|
if (fill_trouble_holes(holes, duplicates, intersections, shape)) {
|
||||||
|
holes.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
holes.clear();
|
||||||
holes.reserve(intersections.size() + duplicates.size());
|
holes.reserve(intersections.size() + duplicates.size());
|
||||||
|
|
||||||
remove_spikes_in_duplicates(shape, duplicates);
|
remove_spikes_in_duplicates(shape, duplicates);
|
||||||
@ -591,11 +650,8 @@ bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
|
|||||||
holes.push_back(hole);
|
holes.push_back(hole);
|
||||||
}
|
}
|
||||||
|
|
||||||
shape = Slic3r::diff_ex(shape, holes, ApplySafetyOffset::Yes);
|
shape = Slic3r::diff_ex(shape, holes, ApplySafetyOffset::No);
|
||||||
|
// ApplySafetyOffset::Yes is incompatible with function fill_trouble_holes
|
||||||
// prepare for next loop
|
|
||||||
holes.clear();
|
|
||||||
intersections.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//priv::visualize_heal("C:/data/temp/heal.svg", shape);
|
//priv::visualize_heal("C:/data/temp/heal.svg", shape);
|
||||||
@ -736,8 +792,12 @@ std::optional<Glyph> priv::get_glyph(const stbtt_fontinfo &font_info, int unicod
|
|||||||
std::reverse(pts.begin(), pts.end());
|
std::reverse(pts.begin(), pts.end());
|
||||||
glyph_polygons.emplace_back(pts);
|
glyph_polygons.emplace_back(pts);
|
||||||
}
|
}
|
||||||
if (!glyph_polygons.empty())
|
if (!glyph_polygons.empty()) {
|
||||||
glyph.shape = Emboss::heal_shape(glyph_polygons);
|
// TrueTypeFonts use non zero winding number
|
||||||
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01
|
||||||
|
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
||||||
|
glyph.shape = Emboss::heal_polygons(glyph_polygons);
|
||||||
|
}
|
||||||
return glyph;
|
return glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1238,7 +1298,22 @@ ExPolygons Slic3r::union_ex(const ExPolygonsWithIds &shapes)
|
|||||||
expolygons_append(result, shape.expoly);
|
expolygons_append(result, shape.expoly);
|
||||||
}
|
}
|
||||||
result = union_ex(result);
|
result = union_ex(result);
|
||||||
heal_shape(result);
|
bool is_healed = heal_expolygons(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExPolygons Slic3r::union_with_delta(const ExPolygonsWithIds &shapes, float delta)
|
||||||
|
{
|
||||||
|
// unify to one expolygons
|
||||||
|
ExPolygons expolygons;
|
||||||
|
for (const ExPolygonsWithId &shape : shapes) {
|
||||||
|
if (shape.expoly.empty())
|
||||||
|
continue;
|
||||||
|
expolygons_append(expolygons, offset_ex(shape.expoly, delta));
|
||||||
|
}
|
||||||
|
ExPolygons result = union_ex(expolygons);
|
||||||
|
result = offset_ex(result, -delta);
|
||||||
|
bool is_healed = heal_expolygons(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,9 +166,9 @@ namespace Emboss
|
|||||||
/// Fix duplicit points and self intersections in polygons.
|
/// Fix duplicit points and self intersections in polygons.
|
||||||
/// Also try to reduce amount of points and remove useless polygon parts
|
/// Also try to reduce amount of points and remove useless polygon parts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="precision">Define wanted precision of shape after heal</param>
|
/// <param name="is_non_zero">Fill type ClipperLib::pftNonZero for overlapping otherwise </param>
|
||||||
/// <returns>Healed shapes</returns>
|
/// <returns>Healed shapes</returns>
|
||||||
ExPolygons heal_shape(const Polygons &shape);
|
ExPolygons heal_polygons(const Polygons &shape, bool is_non_zero = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// NOTE: call Slic3r::union_ex before this call
|
/// NOTE: call Slic3r::union_ex before this call
|
||||||
@ -182,7 +182,7 @@ namespace Emboss
|
|||||||
/// <param name="max_iteration">Heal could create another issue,
|
/// <param name="max_iteration">Heal could create another issue,
|
||||||
/// After healing it is checked again until shape is good or maximal count of iteration</param>
|
/// After healing it is checked again until shape is good or maximal count of iteration</param>
|
||||||
/// <returns>True when shapes is good otherwise False</returns>
|
/// <returns>True when shapes is good otherwise False</returns>
|
||||||
bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10);
|
bool heal_expolygons(ExPolygons &shape, unsigned max_iteration = 10);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Divide line segments in place near to point
|
/// Divide line segments in place near to point
|
||||||
@ -467,6 +467,8 @@ void translate(ExPolygonsWithIds &e, const Point &p);
|
|||||||
BoundingBox get_extents(const ExPolygonsWithIds &e);
|
BoundingBox get_extents(const ExPolygonsWithIds &e);
|
||||||
void center(ExPolygonsWithIds &e);
|
void center(ExPolygonsWithIds &e);
|
||||||
ExPolygons union_ex(const ExPolygonsWithIds &shapes);
|
ExPolygons union_ex(const ExPolygonsWithIds &shapes);
|
||||||
|
// delta .. safe offset before union (use as boolean close)
|
||||||
|
// NOTE: remove unprintable spaces between neighbor curves (made by linearization of curve)
|
||||||
|
ExPolygons union_with_delta(const ExPolygonsWithIds &shapes, float delta);
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
#endif // slic3r_Emboss_hpp_
|
#endif // slic3r_Emboss_hpp_
|
||||||
|
@ -53,11 +53,6 @@ ExPolygonsWithIds create_shape_with_ids(const NSVGimage &image, const NSVGLinePa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// heal shapes
|
|
||||||
if (param.max_heal_iteration > 0)
|
|
||||||
for (auto &[id, expoly] : result)
|
|
||||||
Slic3r::Emboss::heal_shape(expoly, param.max_heal_iteration);
|
|
||||||
|
|
||||||
// SVG is used as centered
|
// SVG is used as centered
|
||||||
// Do not disturb user by settings of pivot position
|
// Do not disturb user by settings of pivot position
|
||||||
center(result);
|
center(result);
|
||||||
@ -366,11 +361,12 @@ ExPolygons fill_to_expolygons(const LinesPath &lines_path, const NSVGshape &shap
|
|||||||
if (fill.empty())
|
if (fill.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero;
|
|
||||||
// if (shape->fillRule == NSVGfillRule::NSVG_FILLRULE_NONZERO)
|
// if (shape->fillRule == NSVGfillRule::NSVG_FILLRULE_NONZERO)
|
||||||
|
bool is_non_zero = true;
|
||||||
if (shape.fillRule == NSVGfillRule::NSVG_FILLRULE_EVENODD)
|
if (shape.fillRule == NSVGfillRule::NSVG_FILLRULE_EVENODD)
|
||||||
fill_type = ClipperLib::pftEvenOdd;
|
is_non_zero = false;
|
||||||
return union_ex(fill, fill_type);
|
|
||||||
|
return Emboss::heal_polygons(fill, is_non_zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DashesParam{
|
struct DashesParam{
|
||||||
@ -518,7 +514,7 @@ ExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &sh
|
|||||||
polygons_append(result, offset(lines_path.polylines, stroke_width / 2, join_type, mitter, end_type));
|
polygons_append(result, offset(lines_path.polylines, stroke_width / 2, join_type, mitter, end_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
return union_ex(result, ClipperLib::pftNonZero);
|
return Emboss::heal_polygons(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
@ -32,9 +32,6 @@ struct NSVGLineParams
|
|||||||
// NOTE: Every point coor from image(float) is multiplied by scale and rounded to integer --> Slic3r::Point
|
// NOTE: Every point coor from image(float) is multiplied by scale and rounded to integer --> Slic3r::Point
|
||||||
double scale = 1. / SCALING_FACTOR;
|
double scale = 1. / SCALING_FACTOR;
|
||||||
|
|
||||||
// count of iteration to heal shape
|
|
||||||
unsigned max_heal_iteration = 10;
|
|
||||||
|
|
||||||
// Flag wether y is negative, when true than y coor is multiplied by -1
|
// Flag wether y is negative, when true than y coor is multiplied by -1
|
||||||
bool is_y_negative = true;
|
bool is_y_negative = true;
|
||||||
|
|
||||||
|
@ -792,7 +792,8 @@ bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread)
|
|||||||
template<typename Fnc>
|
template<typename Fnc>
|
||||||
ExPolygons create_shape(DataBase &input, Fnc was_canceled) {
|
ExPolygons create_shape(DataBase &input, Fnc was_canceled) {
|
||||||
const EmbossShape &es = input.create_shape();
|
const EmbossShape &es = input.create_shape();
|
||||||
return union_ex(es.shapes_with_ids);
|
float delta = 50.f;
|
||||||
|
return union_with_delta(es.shapes_with_ids, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define STORE_SAMPLING
|
//#define STORE_SAMPLING
|
||||||
|
4
tests/data/contour_neighbor.svg
Normal file
4
tests/data/contour_neighbor.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#ed6b21" d="m 68.34,199.06 c 0,6.2 -5.04,11.24 -11.24,11.24 l 0.03507,-11.27073 z" />
|
||||||
|
<path fill="#808080" d="m 68.34,199.06 5.608583,1.97096 -14.648722,13.67 L 57.1,210.3 c 6.2,0 11.24,-5.04 11.24,-11.24 z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 271 B |
@ -203,7 +203,7 @@ ExPolygons heal_and_check(const Polygons &polygons)
|
|||||||
Points polygons_points = to_points(polygons);
|
Points polygons_points = to_points(polygons);
|
||||||
Points duplicits_prev = collect_duplicates(polygons_points);
|
Points duplicits_prev = collect_duplicates(polygons_points);
|
||||||
|
|
||||||
ExPolygons shape = Emboss::heal_shape(polygons);
|
ExPolygons shape = Emboss::heal_polygons(polygons);
|
||||||
|
|
||||||
// Is default shape for unhealabled shape?
|
// Is default shape for unhealabled shape?
|
||||||
bool is_default_shape =
|
bool is_default_shape =
|
||||||
@ -286,6 +286,30 @@ TEST_CASE("Heal of 'm' in Allura_Script.ttf", "[Emboss]")
|
|||||||
auto a = heal_and_check(polygons);
|
auto a = heal_and_check(polygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "libslic3r/NSVGUtils.hpp"
|
||||||
|
TEST_CASE("Heal of svg contour overlap", "[Emboss]") {
|
||||||
|
|
||||||
|
std::string svg_file = "contour_neighbor.svg";
|
||||||
|
auto image = nsvgParseFromFile(TEST_DATA_DIR PATH_SEPARATOR + svg_file, "mm");
|
||||||
|
NSVGLineParams param(1e10);
|
||||||
|
ExPolygonsWithIds shapes = create_shape_with_ids(*image, param);
|
||||||
|
Polygons polygons;
|
||||||
|
for (ExPolygonsWithId &shape : shapes)
|
||||||
|
polygons.push_back(shape.expoly.front().contour);
|
||||||
|
auto a = heal_and_check(polygons);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input contour is extracted from case above "contour_neighbor.svg" with trouble shooted scale
|
||||||
|
TEST_CASE("Heal of overlaping contour", "[Emboss]"){
|
||||||
|
// Extracted from svg:
|
||||||
|
Points contour{{2228926, 1543620}, {745002, 2065101}, {745002, 2065094}, {744990, 2065094}, {684487, 1466338},
|
||||||
|
{510999, 908378}, {236555, 403250}, {-126813, -37014}, {-567074, -400382}, {-1072201, -674822},
|
||||||
|
{-567074, -400378}, {-126813, -37010}, {236555, 403250}, {510999, 908382}, {684487, 1466346},
|
||||||
|
{744990, 2065105}, {-2219648, 2073234}, {-2228926, -908814}, {-1646879, -2073235}};
|
||||||
|
ExPolygons shapes = {ExPolygon{contour}};
|
||||||
|
CHECK(Emboss::heal_expolygons(shapes));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Heal of points close to line", "[Emboss]")
|
TEST_CASE("Heal of points close to line", "[Emboss]")
|
||||||
{
|
{
|
||||||
std::string file_name = "points_close_to_line.svg";
|
std::string file_name = "points_close_to_line.svg";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user