mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-09 07:21:49 +08:00
Bevel only tip of spike which ends in duplicit point
This commit is contained in:
parent
badbe9ddba
commit
8e35ece403
@ -75,11 +75,16 @@ struct SpikeDesc
|
|||||||
// Half of Wanted bevel size
|
// Half of Wanted bevel size
|
||||||
double half_bevel;
|
double half_bevel;
|
||||||
|
|
||||||
SpikeDesc(double bevel_size) {
|
/// <summary>
|
||||||
|
/// Calculate spike description
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
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 length = 6; // has to be smaller than count of heal iteration
|
double angle = 2. * atan2(pixel_spike_length, .5); // [rad]
|
||||||
double angle = 2. * atan2(length, .5); // [rad]
|
|
||||||
cos_angle = std::fabs(cos(angle));
|
cos_angle = std::fabs(cos(angle));
|
||||||
|
|
||||||
// When remove spike this angle is set.
|
// When remove spike this angle is set.
|
||||||
@ -94,6 +99,10 @@ struct SpikeDesc
|
|||||||
void remove_spikes(Polygon &polygon, const SpikeDesc &spike_desc);
|
void remove_spikes(Polygon &polygon, const SpikeDesc &spike_desc);
|
||||||
void remove_spikes(Polygons &polygons, const SpikeDesc &spike_desc);
|
void remove_spikes(Polygons &polygons, const SpikeDesc &spike_desc);
|
||||||
void remove_spikes(ExPolygons &expolygons, const SpikeDesc &spike_desc);
|
void remove_spikes(ExPolygons &expolygons, const SpikeDesc &spike_desc);
|
||||||
|
// return TRUE when remove point. It could create polygon with 2 points.
|
||||||
|
bool remove_when_spike(Polygon &polygon, size_t index, const SpikeDesc &spike_desc);
|
||||||
|
|
||||||
|
void remove_spikes_in_duplicates(ExPolygons &expolygons, const Points &duplicates);
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <Geometry.hpp>
|
#include <Geometry.hpp>
|
||||||
@ -165,8 +174,8 @@ void priv::remove_spikes(Polygon &polygon, const SpikeDesc &spike_desc)
|
|||||||
bool is_ba_short = ba_size_sq < wanted_size_sq;
|
bool is_ba_short = ba_size_sq < wanted_size_sq;
|
||||||
bool is_bc_short = bc_size_sq < wanted_size_sq;
|
bool is_bc_short = bc_size_sq < wanted_size_sq;
|
||||||
auto a_side = [&b, &ba_f, &ba_size_sq, &wanted_size]() {
|
auto a_side = [&b, &ba_f, &ba_size_sq, &wanted_size]() {
|
||||||
Vec2d ab_norm = ba_f / sqrt(ba_size_sq);
|
Vec2d ba_norm = ba_f / sqrt(ba_size_sq);
|
||||||
return b + (wanted_size * ab_norm).cast<coord_t>();
|
return b + (wanted_size * ba_norm).cast<coord_t>();
|
||||||
};
|
};
|
||||||
auto c_side = [&b, &bc_f, &bc_size_sq, &wanted_size]() {
|
auto c_side = [&b, &bc_f, &bc_size_sq, &wanted_size]() {
|
||||||
Vec2d bc_norm = bc_f / sqrt(bc_size_sq);
|
Vec2d bc_norm = bc_f / sqrt(bc_size_sq);
|
||||||
@ -234,6 +243,130 @@ void priv::remove_spikes(Polygon &polygon, const SpikeDesc &spike_desc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool priv::remove_when_spike(Polygon &polygon, size_t index, const SpikeDesc &spike_desc) {
|
||||||
|
Points &pts = polygon.points;
|
||||||
|
const Point &a = (index == 0) ? pts.back() : pts[index-1];
|
||||||
|
const Point &b = pts[index];
|
||||||
|
const Point &c = (&b == &pts.back())? pts.front() : pts[index+1];
|
||||||
|
|
||||||
|
// calc sides
|
||||||
|
Vec2d ba = (a - b).cast<double>();
|
||||||
|
Vec2d bc = (c - b).cast<double>();
|
||||||
|
|
||||||
|
double dot_product = ba.dot(bc);
|
||||||
|
|
||||||
|
// sqrt together after multiplication save one sqrt
|
||||||
|
double ba_size_sq = ba.squaredNorm();
|
||||||
|
double bc_size_sq = bc.squaredNorm();
|
||||||
|
double norm = sqrt(ba_size_sq * bc_size_sq);
|
||||||
|
double cos_angle = dot_product / norm;
|
||||||
|
|
||||||
|
// small angle are around 1 --> cos(0) = 1
|
||||||
|
if (cos_angle < spike_desc.cos_angle)
|
||||||
|
return false; // not a spike
|
||||||
|
|
||||||
|
// has to be in range <-1, 1>
|
||||||
|
// Due to preccission of floating point number could be sligtly out of range
|
||||||
|
if (cos_angle > 1.)
|
||||||
|
cos_angle = 1.;
|
||||||
|
//if (cos_angle < -1.)
|
||||||
|
// cos_angle = -1.;
|
||||||
|
|
||||||
|
// Current Spike angle
|
||||||
|
double angle = acos(cos_angle);
|
||||||
|
double wanted_size = spike_desc.half_bevel / cos(angle / 2.);
|
||||||
|
double wanted_size_sq = wanted_size * wanted_size;
|
||||||
|
|
||||||
|
bool is_ba_short = ba_size_sq < wanted_size_sq;
|
||||||
|
bool is_bc_short = bc_size_sq < wanted_size_sq;
|
||||||
|
|
||||||
|
auto a_side = [&b, &ba, &ba_size_sq, &wanted_size]() {
|
||||||
|
Vec2d ba_norm = ba / sqrt(ba_size_sq);
|
||||||
|
return b + (wanted_size * ba_norm).cast<coord_t>();
|
||||||
|
};
|
||||||
|
auto c_side = [&b, &bc, &bc_size_sq, &wanted_size]() {
|
||||||
|
Vec2d bc_norm = bc / sqrt(bc_size_sq);
|
||||||
|
return b + (wanted_size * bc_norm).cast<coord_t>();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_ba_short && is_bc_short) {
|
||||||
|
// remove short spike
|
||||||
|
pts.erase(pts.begin() + index);
|
||||||
|
return true;
|
||||||
|
} else if (is_ba_short) {
|
||||||
|
// move point B on C-side
|
||||||
|
pts[index] = c_side();
|
||||||
|
} else if (is_bc_short) {
|
||||||
|
// move point B on A-side
|
||||||
|
pts[index] = a_side();
|
||||||
|
} else {
|
||||||
|
// move point B on C-side and add point on A-side(left - before)
|
||||||
|
pts[index] = c_side();
|
||||||
|
Point add = a_side();
|
||||||
|
if (add == pts[index]) {
|
||||||
|
// should be very rare, when SpikeDesc has small base
|
||||||
|
// will be fixed by remove B point
|
||||||
|
pts.erase(pts.begin() + index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pts.insert(pts.begin() + index, add);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void priv::remove_spikes_in_duplicates(ExPolygons &expolygons, const Points &duplicates) {
|
||||||
|
|
||||||
|
auto check = [](Polygon &polygon, const Point &d) -> bool {
|
||||||
|
double spike_bevel = 1 / SHAPE_SCALE;
|
||||||
|
double spike_length = 5.;
|
||||||
|
const static SpikeDesc sd(spike_bevel, spike_length);
|
||||||
|
Points& pts = polygon.points;
|
||||||
|
bool exist_remove = false;
|
||||||
|
for (size_t i = 0; i < pts.size(); i++) {
|
||||||
|
if (pts[i] != d)
|
||||||
|
continue;
|
||||||
|
exist_remove |= remove_when_spike(polygon, i, sd);
|
||||||
|
}
|
||||||
|
return exist_remove && pts.size() < 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool exist_remove = false;
|
||||||
|
for (ExPolygon &expolygon : expolygons) {
|
||||||
|
BoundingBox bb(to_points(expolygon.contour));
|
||||||
|
for (const Point &d : duplicates) {
|
||||||
|
if (!bb.contains(d))
|
||||||
|
continue;
|
||||||
|
exist_remove |= check(expolygon.contour, d);
|
||||||
|
for (Polygon &hole : expolygon.holes)
|
||||||
|
exist_remove |= check(hole, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exist_remove)
|
||||||
|
remove_bad(expolygons);
|
||||||
|
|
||||||
|
// erase invalid ExPolygon after removing
|
||||||
|
//expolygons.erase(std::remove_if(expolygons.begin(), expolygons.end(),
|
||||||
|
// [&duplicates, &check](ExPolygon &expolygon) {
|
||||||
|
// BoundingBox bb(to_points(expolygon.contour));
|
||||||
|
// for (const Point &d : duplicates) {
|
||||||
|
// if (!bb.contains(d))
|
||||||
|
// continue;
|
||||||
|
// if(check(expolygon.contour, d))
|
||||||
|
// return true;
|
||||||
|
|
||||||
|
// // erase invalid hole after removing
|
||||||
|
// Polygons &holes = expolygon.holes;
|
||||||
|
// holes.erase(std::remove_if(holes.begin(), holes.end(),
|
||||||
|
// [&check, &d](Polygon &hole) {
|
||||||
|
// return check(hole, d);
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
|
//}));
|
||||||
|
}
|
||||||
|
|
||||||
void priv::remove_spikes(Polygons &polygons, const SpikeDesc &spike_desc)
|
void priv::remove_spikes(Polygons &polygons, const SpikeDesc &spike_desc)
|
||||||
{
|
{
|
||||||
for (Polygon &polygon : polygons)
|
for (Polygon &polygon : polygons)
|
||||||
@ -555,15 +688,8 @@ ExPolygons Emboss::heal_shape(const Polygons &shape, double precision)
|
|||||||
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());
|
||||||
|
|
||||||
// use douglas peucker to reduce amount of used points
|
// Do not remove all duplicates but do it better way
|
||||||
for (Polygon &polygon : polygons)
|
|
||||||
polygon.douglas_peucker(precision);
|
|
||||||
|
|
||||||
priv::SpikeDesc spike(precision);
|
|
||||||
priv::remove_spikes(polygons, spike);
|
|
||||||
|
|
||||||
// Do not remove all duplicits but do it better way
|
|
||||||
// Overlap all duplicit points by rectangle 3x3
|
// Overlap all duplicit points by rectangle 3x3
|
||||||
Points duplicits = collect_duplicates(to_points(polygons));
|
Points duplicits = collect_duplicates(to_points(polygons));
|
||||||
if (!duplicits.empty()) {
|
if (!duplicits.empty()) {
|
||||||
@ -580,11 +706,6 @@ ExPolygons Emboss::heal_shape(const Polygons &shape, double precision)
|
|||||||
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
||||||
ExPolygons res = Slic3r::union_ex(polygons, ClipperLib::pftNonZero);
|
ExPolygons res = Slic3r::union_ex(polygons, ClipperLib::pftNonZero);
|
||||||
|
|
||||||
priv::remove_spikes(polygons, spike);
|
|
||||||
|
|
||||||
double min_area = precision * precision;
|
|
||||||
priv::remove_small_islands(res, min_area);
|
|
||||||
|
|
||||||
heal_shape(res);
|
heal_shape(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -638,15 +759,17 @@ bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
|
|||||||
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());
|
||||||
|
|
||||||
Points duplicits = collect_duplicates(to_points(shape));
|
Points duplicates = collect_duplicates(to_points(shape));
|
||||||
// duplicits are already uniqua and sorted
|
// duplicates are already uniqua and sorted
|
||||||
|
|
||||||
// Check whether shape is already healed
|
// Check whether shape is already healed
|
||||||
if (intersections.empty() && duplicits.empty())
|
if (intersections.empty() && duplicates.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
assert(holes.empty());
|
assert(holes.empty());
|
||||||
holes.reserve(intersections.size() + duplicits.size());
|
holes.reserve(intersections.size() + duplicates.size());
|
||||||
|
|
||||||
|
remove_spikes_in_duplicates(shape, duplicates);
|
||||||
|
|
||||||
// Fix self intersection in result by subtracting hole 2x2
|
// Fix self intersection in result by subtracting hole 2x2
|
||||||
for (const Point &p : intersections) {
|
for (const Point &p : intersections) {
|
||||||
@ -656,7 +779,7 @@ bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fix duplicit points by hole 3x3 around duplicit point
|
// Fix duplicit points by hole 3x3 around duplicit point
|
||||||
for (const Point &p : duplicits) {
|
for (const Point &p : duplicates) {
|
||||||
Polygon hole(priv::pts_3x3);
|
Polygon hole(priv::pts_3x3);
|
||||||
hole.translate(p);
|
hole.translate(p);
|
||||||
holes.push_back(hole);
|
holes.push_back(hole);
|
||||||
@ -693,7 +816,7 @@ bool priv::heal_dupl_inter2(ExPolygons &shape, unsigned max_iteration) {
|
|||||||
// double minimal_area = 1000;
|
// double minimal_area = 1000;
|
||||||
// priv::remove_small_islands(shape, minimal_area);
|
// priv::remove_small_islands(shape, minimal_area);
|
||||||
|
|
||||||
// check that duplicits and intersections do NOT exists
|
// check that duplicates and intersections do NOT exists
|
||||||
Points duplicits = collect_duplicates(to_points(shape));
|
Points duplicits = collect_duplicates(to_points(shape));
|
||||||
Pointfs intersections_f = intersection_points(shape);
|
Pointfs intersections_f = intersection_points(shape);
|
||||||
if (duplicits.empty() && intersections_f.empty())
|
if (duplicits.empty() && intersections_f.empty())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user