Field sampling is idependent on translation and rotation of island

This commit is contained in:
Filip Sykala - NTB T15p 2024-11-01 15:24:56 +01:00 committed by Lukas Matena
parent 3df99c16be
commit 4b1c928129
4 changed files with 148 additions and 153 deletions

View File

@ -72,7 +72,7 @@ struct SampleConfig
coord_t outline_sample_distance = 2;
// Maximal distance over Voronoi diagram edges to find closest point during aligning Support point
coord_t max_align_distance = 0; // [nano meter]
coord_t max_align_distance = 0; // [scaled mm -> nanometers]
// There is no need to calculate with precisse island
// NOTE: Slice of Cylinder bottom has tip of trinagles on contour

View File

@ -81,7 +81,7 @@ SampleConfig SampleConfigFactory::create(float support_head_diameter_in_mm)
2 * head_diameter + 2 * result.minimal_distance_from_outline +
max_distance / 2;
result.min_width_for_outline_support = result.max_width_for_center_support_line - 2 * head_diameter;
result.outline_sample_distance = 3*result.max_distance/4;
result.outline_sample_distance = 3 * result.max_distance/4;
// Align support points
// TODO: propagate print resolution

View File

@ -210,30 +210,9 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
return samples;
}
std::vector<Slic3r::Vec2f> SampleIslandUtils::sample_expolygon(
const ExPolygon &expoly, float samples_per_mm2)
{
static const float mm2_area = static_cast<float>(scale_(1) * scale_(1));
// Equilateral triangle area = (side * height) / 2
float triangle_area = mm2_area / samples_per_mm2;
// Triangle area = sqrt(3) / 4 * "triangle side"
static const float coef1 = sqrt(3.) / 4.;
coord_t triangle_side = static_cast<coord_t>(
std::round(sqrt(triangle_area * coef1)));
Points points = sample_expolygon(expoly, triangle_side);
std::vector<Vec2f> result;
result.reserve(points.size());
std::transform(points.begin(), points.end(), std::back_inserter(result),
[](const Point &p)->Vec2f { return unscale(p).cast<float>(); });
return result;
}
Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly,
coord_t triangle_side)
{
Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly, coord_t triangle_side){
const Points &points = expoly.contour.points;
assert(!points.empty());
// get y range
@ -245,8 +224,7 @@ Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly,
else if (max_y < point.y())
max_y = point.y();
}
coord_t triangle_side_2 = triangle_side / 2;
coord_t half_triangle_side = triangle_side / 2;
static const float coef2 = sqrt(3.) / 2.;
coord_t triangle_height = static_cast<coord_t>(std::round(triangle_side * coef2));
@ -271,8 +249,7 @@ Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly,
Points result;
size_t start_index = 0;
bool is_odd = false;
for (coord_t y = min_y + triangle_height / 2; y < max_y;
y += triangle_height) {
for (coord_t y = min_y + triangle_height / 2; y < max_y; y += triangle_height) {
is_odd = !is_odd;
std::vector<coord_t> intersections;
bool increase_start_index = true;
@ -299,11 +276,11 @@ Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly,
for (size_t index = 0; index + 1 < intersections.size(); index += 2) {
coord_t start_x = intersections[index];
coord_t end_x = intersections[index + 1];
if (is_odd) start_x += triangle_side_2;
if (is_odd) start_x += half_triangle_side;
coord_t div = start_x / triangle_side;
if (start_x > 0) div += 1;
coord_t x = div * triangle_side;
if (is_odd) x -= triangle_side_2;
if (is_odd) x -= half_triangle_side;
while (x < end_x) {
result.emplace_back(x, y);
x += triangle_side;
@ -313,6 +290,32 @@ Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly,
return result;
}
Slic3r::Points SampleIslandUtils::sample_expolygon_with_centering(const ExPolygon &expoly, coord_t triangle_side) {
assert(!expoly.contour.empty());
if (expoly.contour.empty())
return {};
// to unify sampling of rotated expolygon offset and rotate pattern by centroid and farrest point
Point center = expoly.contour.centroid();
Point extrem = expoly.contour.front(); // the farest point from center
// NOTE: ignore case with multiple same distance points
double extrem_distance_sq = -1.;
for (const Point &point : expoly.contour.points) {
Point from_center = point - center;
double distance_sq = from_center.cast<double>().squaredNorm();
if (extrem_distance_sq < distance_sq) {
extrem_distance_sq = distance_sq;
extrem = point;
}
}
double angle = atan2(extrem.y() - center.y(), extrem.x() - center.x());
ExPolygon expoly_tr = expoly; // copy
expoly_tr.rotate(angle, center);
Points result = sample_expolygon(expoly_tr, triangle_side);
for (Point &point : result)
point.rotate(-angle, center);
return result;
}
SupportIslandPointPtr SampleIslandUtils::create_no_move_point(
const VoronoiGraph::Node::Neighbor *neighbor,
double ratio,
@ -1090,13 +1093,13 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
// 2) Two support points have to stretch island even if haed is not fully under island.
if (path.length < config.max_length_for_two_support_points) {
coord_t max_distance_by_length = static_cast<coord_t>(path.length / 4);
coord_t max_distance = std::min(config.half_distance, max_distance_by_length);
coord_t max_distance = std::min(config.maximal_distance_from_outline, max_distance_by_length);
// Be carefull tiny island could contain overlapped support points
assert(max_distance < (static_cast<coord_t>(path.length / 2) - config.head_radius));
coord_t min_width = 2 * config.head_radius; //minimal_distance_from_outline;
return create_side_points(path.nodes, lines, min_width, max_distance);
return create_side_points(path.nodes, lines, min_width, config.maximal_distance_from_outline);
}
// othewise sample path
@ -1144,7 +1147,7 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
// IMPROVE: check side branches on start path
} else {
// start sample field
VoronoiGraph::Position field_start{neighbor, .1e-5};
VoronoiGraph::Position field_start{neighbor, 0.f};
sample_field(field_start, points, center_starts, done, lines, config);
}
@ -1166,18 +1169,14 @@ void SampleIslandUtils::sample_field(const VoronoiGraph::Position &field_start,
const SampleConfig &config)
{
auto field = create_field(field_start, center_starts, done, lines, config);
if (field.inner.empty())
return; // no inner part
SupportIslandPoints outline_support = sample_outline(field, config);
points.insert(points.end(), std::move_iterator(outline_support.begin()),
std::move_iterator(outline_support.end()));
// Erode island to not sampled island around border,
// minimal value must be -config.minimal_distance_from_outline
Polygons polygons = offset(field.border,
-2.f * config.minimal_distance_from_outline,
ClipperLib::jtSquare);
if (polygons.empty()) return;
// Inner must survive after sample field for aligning supports(move along outline)
auto inner = std::make_shared<ExPolygon>(field.inner);
Points inner_points = sample_expolygon(*inner, config.max_distance);
Points inner_points = sample_expolygon_with_centering(*inner, config.max_distance);
std::transform(inner_points.begin(), inner_points.end(), std::back_inserter(points),
[&](const Point &point) {
return std::make_unique<SupportIslandInnerPoint>(
@ -1327,7 +1326,7 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
// create field start
auto result = VoronoiGraphUtils::get_position_with_width(
start.neighbor, config.min_width_for_outline_support, lines
start.neighbor, config.max_width_for_center_support_line, lines
);
// sample rest of neighbor before field
@ -1449,19 +1448,11 @@ bool SampleIslandUtils::create_sample_center_end(
return true;
}
SampleIslandUtils::Field SampleIslandUtils::create_field(
const VoronoiGraph::Position & field_start,
CenterStarts & tiny_starts,
std::set<const VoronoiGraph::Node *> &tiny_done,
const Lines & lines,
const SampleConfig &config)
{
using VD = Slic3r::Geometry::VoronoiDiagram;
// DTO represents one island change from wide to tiny part
// it is stored inside map under source line index
struct WideTinyChange{
// Help functions for create field
namespace {
// Data type object represents one island change from wide to tiny part
// It is stored inside map under source line index
struct WideTinyChange{
// new coordinate for line.b point
Point new_b;
// new coordinate for next line.a point
@ -1486,8 +1477,83 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
return compare.compare(left.new_b, right.new_b);
}
};
};
using WideTinyChanges = std::vector<WideTinyChange>;
};
using WideTinyChanges = std::vector<WideTinyChange>;
/// <summary>
/// create offsetted field
/// </summary>
/// <param name="island">source field</param>
/// <param name="offset_delta">distance from outline</param>
/// <returns>offseted field
/// First - offseted island outline
/// Second - map for convert source field index to result border index
/// </returns>
std::pair<Slic3r::ExPolygon, std::map<size_t, size_t>>
outline_offset(const Slic3r::ExPolygon &island, float offset_delta)
{
Polygons polygons = offset(island, -offset_delta, ClipperLib::jtSquare);
if (polygons.empty()) return {}; // no place for support point
assert(polygons.front().is_counter_clockwise());
ExPolygon offseted(polygons.front());
for (size_t i = 1; i < polygons.size(); ++i) {
Polygon &hole = polygons[i];
assert(hole.is_clockwise());
offseted.holes.push_back(hole);
}
// TODO: Connect indexes for convert during creation of offset
// !! this implementation was fast for develop BUT NOT for running !!
const double angle_tolerace = 1e-4;
const double distance_tolerance = 20.;
Lines island_lines = to_lines(island);
Lines offset_lines = to_lines(offseted);
// Convert index map from island index to offseted index
std::map<size_t, size_t> converter;
for (size_t island_line_index = 0; island_line_index < island_lines.size(); ++island_line_index) {
const Line &island_line = island_lines[island_line_index];
Vec2d dir1 = LineUtils::direction(island_line).cast<double>();
dir1.normalize();
for (size_t offset_line_index = 0; offset_line_index < offset_lines.size(); ++offset_line_index) {
const Line &offset_line = offset_lines[offset_line_index];
Vec2d dir2 = LineUtils::direction(offset_line).cast<double>();
dir2.normalize();
double angle = acos(dir1.dot(dir2));
// not similar direction
if (fabs(angle) > angle_tolerace) continue;
Point offset_middle = LineUtils::middle(offset_line);
Point island_middle = LineUtils::middle(island_line);
Point diff_middle = offset_middle - island_middle;
if (fabs(diff_middle.x()) > 2 * offset_delta ||
fabs(diff_middle.y()) > 2 * offset_delta)
continue;
double distance = island_line.perp_distance_to(offset_middle);
if (fabs(distance - offset_delta) > distance_tolerance)
continue;
// found offseted line
converter[island_line_index] = offset_line_index;
break;
}
}
return {offseted, converter};
}
} // namespace
SampleIslandUtils::Field SampleIslandUtils::create_field(
const VoronoiGraph::Position & field_start,
CenterStarts & tiny_starts,
std::set<const VoronoiGraph::Node *> &tiny_done,
const Lines & lines,
const SampleConfig &config)
{
using VD = Slic3r::Geometry::VoronoiDiagram;
// store shortening of outline segments
// line index, vector<next line index + 2x shortening points>
std::map<size_t, WideTinyChanges> wide_tiny_changes;
@ -1726,6 +1792,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
inser_point_b(outline_index, points, done_indexes);
} while (outline_index != input_index);
assert(points.size() >= 3);
Field field;
field.border.contour = Polygon(points);
// finding holes
@ -1744,8 +1811,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
field.source_indexe_for_change = source_indexe_for_change;
field.source_indexes = std::move(source_indexes);
std::tie(field.inner, field.field_2_inner) =
outline_offset(field.border, config.minimal_distance_from_outline);
outline_offset(field.border, (float)config.minimal_distance_from_outline);
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH
{
const char *source_line_color = "black";
@ -1759,63 +1825,11 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
draw(svg, field, draw_border_line_indexes, draw_field_source_indexes);
}
#endif //SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH
assert(!field.border.empty());
assert(!field.inner.empty());
return field;
}
std::pair<Slic3r::ExPolygon, std::map<size_t, size_t>>
SampleIslandUtils::outline_offset(const Slic3r::ExPolygon &island,
coord_t offset_distance)
{
Polygons polygons = offset(island, -offset_distance, ClipperLib::jtSquare);
if (polygons.empty()) return {}; // no place for support point
assert(polygons.front().is_counter_clockwise());
ExPolygon offseted(polygons.front());
for (size_t i = 1; i < polygons.size(); ++i) {
Polygon &hole = polygons[i];
assert(hole.is_clockwise());
offseted.holes.push_back(hole);
}
// TODO: Connect indexes for convert during creation of offset
// !! this implementation was fast for develop BUT NOT for running !!
const double angle_tolerace = 1e-4;
const double distance_tolerance = 20.;
Lines island_lines = to_lines(island);
Lines offset_lines = to_lines(offseted);
// Convert index map from island index to offseted index
std::map<size_t, size_t> converter;
for (size_t island_line_index = 0; island_line_index < island_lines.size(); ++island_line_index) {
const Line &island_line = island_lines[island_line_index];
Vec2d dir1 = LineUtils::direction(island_line).cast<double>();
dir1.normalize();
for (size_t offset_line_index = 0; offset_line_index < offset_lines.size(); ++offset_line_index) {
const Line &offset_line = offset_lines[offset_line_index];
Vec2d dir2 = LineUtils::direction(offset_line).cast<double>();
dir2.normalize();
double angle = acos(dir1.dot(dir2));
// not similar direction
if (fabs(angle) > angle_tolerace) continue;
Point offset_middle = LineUtils::middle(offset_line);
Point island_middle = LineUtils::middle(island_line);
Point diff_middle = offset_middle - island_middle;
if (fabs(diff_middle.x()) > 2 * offset_distance ||
fabs(diff_middle.y()) > 2 * offset_distance) continue;
double distance = island_line.perp_distance_to(offset_middle);
if (fabs(distance - offset_distance) > distance_tolerance)
continue;
// found offseted line
converter[island_line_index] = offset_line_index;
break;
}
}
return {offseted, converter};
}
SupportIslandPoints SampleIslandUtils::sample_outline(
const Field &field, const SampleConfig &config)
{
@ -1833,7 +1847,7 @@ SupportIslandPoints SampleIslandUtils::sample_outline(
coord_t line_length = static_cast<coord_t>(std::round(line_length_double));
if (last_support + line_length > sample_distance) {
do {
double ratio = (sample_distance - last_support) / line_length_double;
float ratio = static_cast<float>((sample_distance - last_support) / line_length_double);
result.emplace_back(
std::make_unique<SupportOutlineIslandPoint>(
Position(index, ratio), restriction,

View File

@ -34,14 +34,6 @@ public:
const ExPolygon &island, const SampleConfig &config
);
/// <summary>
/// Uniform sample expolygon area by points inside Equilateral triangle center
/// </summary>
/// <param name="expoly">Input area to sample. (scaled)</param>
/// <param name="samples_per_mm2">Density of sampling.</param>
/// <returns>Samples - 2d unscaled coordinates [in mm]</returns>
static std::vector<Vec2f> sample_expolygon(const ExPolygon &expoly, float samples_per_mm2);
/// <summary>
/// Uniform sample expolygon area by points inside Equilateral triangle center
/// </summary>
@ -49,6 +41,8 @@ public:
/// <param name="triangle_side">Distance between samples.</param>
/// <returns>Uniform samples(scaled)</returns>
static Points sample_expolygon(const ExPolygon &expoly, coord_t triangle_side);
/// Offset sampling pattern by centroid and farrest point from centroid
static Points sample_expolygon_with_centering(const ExPolygon &expoly, coord_t triangle_side);
/// <summary>
/// Create support point on edge defined by neighbor
@ -370,19 +364,6 @@ public :
/// <returns>support for outline</returns>
static SupportIslandPoints sample_outline(const Field & field,
const SampleConfig &config);
private:
/// <summary>
/// create offsetted field
/// </summary>
/// <param name="island">source field</param>
/// <param name="offset_distance">distance from outline</param>
/// <returns>offseted field
/// First - offseted island outline
/// Second - map for convert source field index to result border index
/// </returns>
static std::pair<Slic3r::ExPolygon, std::map<size_t, size_t>>
outline_offset(const Slic3r::ExPolygon &island, coord_t offset_distance);
// debug draw functions
public :
static bool is_visualization_disabled();