Move unused implementation behinde macro

This commit is contained in:
Filip Sykala - NTB T15p 2023-01-26 12:00:36 +01:00
parent 8e35ece403
commit f2803c4ae1
3 changed files with 182 additions and 193 deletions

View File

@ -23,6 +23,12 @@ using namespace Slic3r;
using namespace Emboss; using namespace Emboss;
using fontinfo_opt = std::optional<stbtt_fontinfo>; using fontinfo_opt = std::optional<stbtt_fontinfo>;
// for try approach to heal shape by Clipper::Closing
//#define HEAL_WITH_CLOSING
// functionality to remove all spikes from shape
//#define REMOVE_SPIKES
// do not expose out of this file stbtt_ data types // do not expose out of this file stbtt_ data types
namespace priv{ namespace priv{
using Polygon = Slic3r::Polygon; using Polygon = Slic3r::Polygon;
@ -60,8 +66,8 @@ Points collect_close_points(const ExPolygons &expolygons, double distance = .6);
// Heal duplicates points and self intersections // Heal duplicates points and self intersections
bool heal_dupl_inter(ExPolygons &shape, unsigned max_iteration); bool heal_dupl_inter(ExPolygons &shape, unsigned max_iteration);
bool heal_dupl_inter2(ExPolygons &shape, unsigned max_iteration); // by close_ex
// for debug purpose
void visualize_heal(const std::string& svg_filepath, const ExPolygons &expolygons); void visualize_heal(const std::string& svg_filepath, const ExPolygons &expolygons);
const Points pts_2x2({Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)}); const Points pts_2x2({Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)});
@ -93,156 +99,21 @@ struct SpikeDesc
} }
}; };
// 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);
#ifdef REMOVE_SPIKES
// Remove long sharp corners aka spikes // Remove long sharp corners aka spikes
// by adding points to bevel tip of spikes - Not printable parts // by adding points to bevel tip of spikes - Not printable parts
// Try to not modify long sides of spike and add points on it's side // Try to not modify long sides of spike and add points on it's side
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. #endif
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>
void priv::remove_spikes(Polygon &polygon, const SpikeDesc &spike_desc)
{
enum class Type {
add, // Move with point B on A-side and add new point on C-side
move, // Only move with point B
erase // left only points A and C without move
};
struct SpikeHeal
{
Type type;
size_t index;
Point b;
Point add;
};
using SpikeHeals = std::vector<SpikeHeal>;
SpikeHeals heals;
size_t count = polygon.size();
if (count < 3)
return;
const Point *ptr_a = &polygon[count - 2];
const Point *ptr_b = &polygon[count - 1];
for (const Point &c : polygon) {
const Point &a = *ptr_a;
const Point &b = *ptr_b;
ScopeGuard sg([&ptr_a, &ptr_b, &c]() {
// prepare for next loop
ptr_a = ptr_b;
ptr_b = &c;
});
// calc sides
Point ba = a - b;
Point bc = c - b;
Vec2d ba_f = ba.cast<double>();
Vec2d bc_f = bc.cast<double>();
double dot_product = ba_f.dot(bc_f);
// sqrt together after multiplication save one sqrt
double ba_size_sq = ba_f.squaredNorm();
double bc_size_sq = bc_f.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)
continue;
SpikeHeal heal;
heal.index = &b - &polygon.points.front();
// 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_f, &ba_size_sq, &wanted_size]() {
Vec2d ba_norm = ba_f / sqrt(ba_size_sq);
return b + (wanted_size * ba_norm).cast<coord_t>();
};
auto c_side = [&b, &bc_f, &bc_size_sq, &wanted_size]() {
Vec2d bc_norm = bc_f / sqrt(bc_size_sq);
return b + (wanted_size * bc_norm).cast<coord_t>();
};
if (is_ba_short && is_bc_short) {
// remove short spike
heal.type = Type::erase;
} else if (is_ba_short){
// move point B on C-side
heal.type = Type::move;
heal.b = c_side();
} else if (is_bc_short) {
// move point B on A-side
heal.type = Type::move;
heal.b = a_side();
} else {
// move point B on A-side and add point on C-side
heal.type = Type::add;
heal.b = a_side();
heal.add = c_side();
}
heals.push_back(heal);
}
if (heals.empty())
return;
// sort index from high to low
if (heals.front().index == (count - 1))
std::rotate(heals.begin(), heals.begin()+1, heals.end());
std::reverse(heals.begin(), heals.end());
int extend = 0;
int curr_extend = 0;
for (const SpikeHeal &h : heals)
switch (h.type) {
case Type::add:
++curr_extend;
if (extend < curr_extend)
extend = curr_extend;
break;
case Type::erase:
--curr_extend;
}
Points &pts = polygon.points;
if (extend > 0)
pts.reserve(pts.size() + extend);
for (const SpikeHeal &h : heals) {
switch (h.type) {
case Type::add:
pts[h.index] = h.b;
pts.insert(pts.begin() + h.index+1, h.add);
break;
case Type::erase:
pts.erase(pts.begin() + h.index);
break;
case Type::move:
pts[h.index] = h.b;
break;
default: break;
}
}
}
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) {
Points &pts = polygon.points; Points &pts = polygon.points;
const Point &a = (index == 0) ? pts.back() : pts[index-1]; const Point &a = (index == 0) ? pts.back() : pts[index-1];
@ -344,43 +215,6 @@ void priv::remove_spikes_in_duplicates(ExPolygons &expolygons, const Points &dup
if (exist_remove) if (exist_remove)
remove_bad(expolygons); 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)
{
for (Polygon &polygon : polygons)
remove_spikes(polygon, spike_desc);
remove_bad(polygons);
}
void priv::remove_spikes(ExPolygons &expolygons, const SpikeDesc &spike_desc)
{
for (ExPolygon &expolygon : expolygons) {
remove_spikes(expolygon.contour, spike_desc);
remove_spikes(expolygon.holes, spike_desc);
}
remove_bad(expolygons);
} }
bool priv::is_valid(const FontFile &font, unsigned int index) { bool priv::is_valid(const FontFile &font, unsigned int index) {
@ -678,7 +512,7 @@ bool priv::remove_self_intersections(ExPolygons &shape, unsigned max_iteration)
return false; return false;
} }
ExPolygons Emboss::heal_shape(const Polygons &shape, double precision) ExPolygons Emboss::heal_shape(const Polygons &shape)
{ {
// 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
@ -705,7 +539,6 @@ ExPolygons Emboss::heal_shape(const Polygons &shape, double precision)
// https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01 // https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01
// 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);
heal_shape(res); heal_shape(res);
return res; return res;
} }
@ -732,15 +565,13 @@ void priv::visualize_heal(const std::string &svg_filepath, const ExPolygons &exp
bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration) bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
{ {
return priv::heal_dupl_inter(shape, max_iteration); return priv::heal_dupl_inter(shape, max_iteration);
//return priv::heal_dupl_inter2(shape, max_iteration);
} }
#ifndef HEAL_WITH_CLOSING
bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration) bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
{ {
if (shape.empty()) return true; if (shape.empty()) return true;
//priv::visualize_heal("C:/data/temp/heal_prev.svg", shape);
// create loop permanent memory // create loop permanent memory
Polygons holes; Polygons holes;
Points intersections; Points intersections;
@ -798,8 +629,9 @@ bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
shape = {priv::create_bounding_rect(shape)}; shape = {priv::create_bounding_rect(shape)};
return false; return false;
} }
#else
bool priv::heal_dupl_inter2(ExPolygons &shape, unsigned max_iteration) { bool priv::heal_dupl_inter(ExPolygons &shape, unsigned max_iteration)
{
priv::remove_same_neighbor(shape); priv::remove_same_neighbor(shape);
const float delta = 2.f; const float delta = 2.f;
@ -828,6 +660,7 @@ bool priv::heal_dupl_inter2(ExPolygons &shape, unsigned max_iteration) {
shape = {priv::create_bounding_rect(shape)}; shape = {priv::create_bounding_rect(shape)};
return false; return false;
} }
#endif // !HEAL_WITH_CLOSING
ExPolygon priv::create_bounding_rect(const ExPolygons &shape) { ExPolygon priv::create_bounding_rect(const ExPolygons &shape) {
BoundingBox bb = get_extents(shape); BoundingBox bb = get_extents(shape);
@ -930,7 +763,7 @@ std::optional<Glyph> priv::get_glyph(const stbtt_fontinfo &font_info, int unicod
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, flatness / SHAPE_SCALE); glyph.shape = Emboss::heal_shape(glyph_polygons);
return glyph; return glyph;
} }
@ -1772,3 +1605,160 @@ std::optional<Vec2d> Emboss::OrthoProject::unproject(const Vec3d &p, double *dep
if (depth != nullptr) *depth = pp.z(); if (depth != nullptr) *depth = pp.z();
return Vec2d(pp.x(), pp.y()); return Vec2d(pp.x(), pp.y());
} }
#ifdef REMOVE_SPIKES
#include <Geometry.hpp>
void priv::remove_spikes(Polygon &polygon, const SpikeDesc &spike_desc)
{
enum class Type {
add, // Move with point B on A-side and add new point on C-side
move, // Only move with point B
erase // left only points A and C without move
};
struct SpikeHeal
{
Type type;
size_t index;
Point b;
Point add;
};
using SpikeHeals = std::vector<SpikeHeal>;
SpikeHeals heals;
size_t count = polygon.size();
if (count < 3)
return;
const Point *ptr_a = &polygon[count - 2];
const Point *ptr_b = &polygon[count - 1];
for (const Point &c : polygon) {
const Point &a = *ptr_a;
const Point &b = *ptr_b;
ScopeGuard sg([&ptr_a, &ptr_b, &c]() {
// prepare for next loop
ptr_a = ptr_b;
ptr_b = &c;
});
// calc sides
Point ba = a - b;
Point bc = c - b;
Vec2d ba_f = ba.cast<double>();
Vec2d bc_f = bc.cast<double>();
double dot_product = ba_f.dot(bc_f);
// sqrt together after multiplication save one sqrt
double ba_size_sq = ba_f.squaredNorm();
double bc_size_sq = bc_f.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)
continue;
SpikeHeal heal;
heal.index = &b - &polygon.points.front();
// 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_f, &ba_size_sq, &wanted_size]() {
Vec2d ba_norm = ba_f / sqrt(ba_size_sq);
return b + (wanted_size * ba_norm).cast<coord_t>();
};
auto c_side = [&b, &bc_f, &bc_size_sq, &wanted_size]() {
Vec2d bc_norm = bc_f / sqrt(bc_size_sq);
return b + (wanted_size * bc_norm).cast<coord_t>();
};
if (is_ba_short && is_bc_short) {
// remove short spike
heal.type = Type::erase;
} else if (is_ba_short){
// move point B on C-side
heal.type = Type::move;
heal.b = c_side();
} else if (is_bc_short) {
// move point B on A-side
heal.type = Type::move;
heal.b = a_side();
} else {
// move point B on A-side and add point on C-side
heal.type = Type::add;
heal.b = a_side();
heal.add = c_side();
}
heals.push_back(heal);
}
if (heals.empty())
return;
// sort index from high to low
if (heals.front().index == (count - 1))
std::rotate(heals.begin(), heals.begin()+1, heals.end());
std::reverse(heals.begin(), heals.end());
int extend = 0;
int curr_extend = 0;
for (const SpikeHeal &h : heals)
switch (h.type) {
case Type::add:
++curr_extend;
if (extend < curr_extend)
extend = curr_extend;
break;
case Type::erase:
--curr_extend;
}
Points &pts = polygon.points;
if (extend > 0)
pts.reserve(pts.size() + extend);
for (const SpikeHeal &h : heals) {
switch (h.type) {
case Type::add:
pts[h.index] = h.b;
pts.insert(pts.begin() + h.index+1, h.add);
break;
case Type::erase:
pts.erase(pts.begin() + h.index);
break;
case Type::move:
pts[h.index] = h.b;
break;
default: break;
}
}
}
void priv::remove_spikes(Polygons &polygons, const SpikeDesc &spike_desc)
{
for (Polygon &polygon : polygons)
remove_spikes(polygon, spike_desc);
remove_bad(polygons);
}
void priv::remove_spikes(ExPolygons &expolygons, const SpikeDesc &spike_desc)
{
for (ExPolygon &expolygon : expolygons) {
remove_spikes(expolygon.contour, spike_desc);
remove_spikes(expolygon.holes, spike_desc);
}
remove_bad(expolygons);
}
#endif // REMOVE_SPIKES

View File

@ -157,10 +157,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="shape">Input shape to heal</param>
/// <param name="precision">Define wanted precision of shape after heal</param> /// <param name="precision">Define wanted precision of shape after heal</param>
/// <returns>Healed shapes</returns> /// <returns>Healed shapes</returns>
ExPolygons heal_shape(const Polygons &shape, double precision); ExPolygons heal_shape(const Polygons &shape);
/// <summary> /// <summary>
/// NOTE: call Slic3r::union_ex before this call /// NOTE: call Slic3r::union_ex before this call

View File

@ -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, 10); ExPolygons shape = Emboss::heal_shape(polygons);
// Is default shape for unhealabled shape? // Is default shape for unhealabled shape?
bool is_default_shape = bool is_default_shape =