mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-13 21:06:00 +08:00
Fix for sampling and enhance visualization.
This commit is contained in:
parent
73f8583e18
commit
688c614e47
@ -124,9 +124,10 @@ SVG draw_island(const std::string &path, const ExPolygon &island, const ExPolygo
|
||||
}
|
||||
SVG draw_island_graph(const std::string &path, const ExPolygon &island,
|
||||
const ExPolygon &simplified_island, const VoronoiGraph& skeleton,
|
||||
const VoronoiGraph::ExPath& longest_path, const Lines& lines, coord_t width) {
|
||||
const VoronoiGraph::ExPath& longest_path, const Lines& lines, const SampleConfig &config) {
|
||||
SVG svg = draw_island(path, island, simplified_island);
|
||||
VoronoiGraphUtils::draw(svg, skeleton, lines, width);
|
||||
VoronoiGraphUtils::draw(svg, skeleton, lines, config, true /*print Pointer address*/);
|
||||
coord_t width = config.head_radius / 10;
|
||||
VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "orange");
|
||||
return svg;
|
||||
}
|
||||
@ -176,7 +177,7 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
|
||||
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
|
||||
|
||||
#ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image
|
||||
if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10);
|
||||
if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
|
||||
#endif // OPTION_TO_STORE_ISLAND
|
||||
|
||||
SupportIslandPoints samples = sample_expath(longest_path, lines, config);
|
||||
@ -184,7 +185,7 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
|
||||
#ifdef OPTION_TO_STORE_ISLAND
|
||||
Points samples_before_align = to_points(samples);
|
||||
if (!path.empty()) {
|
||||
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10);
|
||||
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
|
||||
draw(svg, samples, config.head_radius);
|
||||
}
|
||||
#endif // OPTION_TO_STORE_ISLAND
|
||||
@ -193,13 +194,17 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
|
||||
SampleIslandUtils::align_samples(samples, island, config);
|
||||
#ifdef OPTION_TO_STORE_ISLAND
|
||||
if (!path.empty()) {
|
||||
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10);
|
||||
draw(svg, samples, config.head_radius);
|
||||
SVG svg = draw_island(path, island, simplified_island);
|
||||
coord_t width = config.head_radius / 5;
|
||||
VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "darkorange");
|
||||
VoronoiGraphUtils::draw(svg, skeleton, lines, config, false /*print Pointer address*/);
|
||||
|
||||
Lines align_moves;
|
||||
align_moves.reserve(samples.size());
|
||||
for (size_t i = 0; i < samples.size(); ++i)
|
||||
align_moves.push_back(Line(samples[i]->point, samples_before_align[i]));
|
||||
svg.draw(align_moves, "lightgray");
|
||||
svg.draw(align_moves, "lightgray", width);
|
||||
draw(svg, samples, config.head_radius);
|
||||
}
|
||||
#endif // OPTION_TO_STORE_ISLAND
|
||||
return samples;
|
||||
@ -1130,7 +1135,6 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
|
||||
coord_t support_in = config.max_distance + already_supported;
|
||||
center_starts.emplace_back(position->neighbor, support_in, start_path);
|
||||
} else {
|
||||
assert(position.has_value());
|
||||
done.insert(start_node);
|
||||
coord_t support_in = config.minimal_distance_from_outline;
|
||||
center_starts.emplace_back(neighbor, support_in);
|
||||
@ -1263,16 +1267,15 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
|
||||
std::set<const VoronoiGraph::Node *> &done,
|
||||
SupportIslandPoints & results,
|
||||
const Lines & lines,
|
||||
const SampleConfig & config)
|
||||
const SampleConfig & config,
|
||||
// sign that there was added point on start.path
|
||||
// used to distiquish whether add support point on island edge
|
||||
bool is_continous)
|
||||
{
|
||||
// Current place to sample
|
||||
CenterStart start(nullptr, {}, {});
|
||||
if (!pop_start(start, new_starts, done)) return {};
|
||||
|
||||
// sign that there was added point on start.path
|
||||
// used to distiquish whether add support point on island edge
|
||||
bool is_continous = false;
|
||||
|
||||
// Loop over thin part of island which need to be sampled on the voronoi skeleton.
|
||||
while (!is_continous || start.neighbor->max_width() <= config.max_width_for_center_support_line) {
|
||||
assert(done.find(start.neighbor->node) == done.end()); // not proccessed only
|
||||
@ -1489,16 +1492,16 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
|
||||
// line index, vector<next line index + 2x shortening points>
|
||||
std::map<size_t, WideTinyChanges> wide_tiny_changes;
|
||||
|
||||
coord_t minimal_edge_length = std::max(config.max_distance / 2, 2*config.minimal_distance_from_outline);
|
||||
coord_t half_max_distance = config.max_distance / 2;
|
||||
// cut lines at place where neighbor has width = min_width_for_outline_support
|
||||
// neighbor must be in direction from wide part to tiny part of island
|
||||
auto add_wide_tiny_change =
|
||||
[minimal_edge_length, half_max_distance, &wide_tiny_changes,
|
||||
&lines, &tiny_starts, &tiny_done]
|
||||
(const VoronoiGraph::Position &position, const VoronoiGraph::Node *source_node)->bool{
|
||||
if (VoronoiGraphUtils::ends_in_distanace(position, minimal_edge_length))
|
||||
return false; // no change only rich outline
|
||||
// Prepare data for field outline,
|
||||
// when field transit into tiny part of island
|
||||
auto add_wide_tiny_change_only = [&wide_tiny_changes, &lines, &tiny_done]
|
||||
(const VoronoiGraph::Position &position){
|
||||
Point p1, p2;
|
||||
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines);
|
||||
const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor;
|
||||
const VD::edge_type *edge = neighbor->edge;
|
||||
size_t i1 = edge->cell()->source_index();
|
||||
size_t i2 = edge->twin()->cell()->source_index();
|
||||
|
||||
// function to add sorted change from wide to tiny
|
||||
// stored uder line index or line shorten in point b
|
||||
@ -1513,13 +1516,6 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
|
||||
}
|
||||
};
|
||||
|
||||
Point p1, p2;
|
||||
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines);
|
||||
const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor;
|
||||
const VD::edge_type *edge = neighbor->edge;
|
||||
size_t i1 = edge->cell()->source_index();
|
||||
size_t i2 = edge->twin()->cell()->source_index();
|
||||
|
||||
const Line &l1 = lines[i1];
|
||||
if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) {
|
||||
// line1 is shorten on side line1.a --> line2 is shorten on side line2.b
|
||||
@ -1528,8 +1524,22 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
|
||||
// line1 is shorten on side line1.b
|
||||
add(p1, p2, i1, i2);
|
||||
}
|
||||
coord_t support_in = neighbor->length() * position.ratio + half_max_distance;
|
||||
CenterStart tiny_start(neighbor, support_in, {source_node});
|
||||
};
|
||||
|
||||
coord_t minimal_edge_length = std::max(config.max_distance / 2, 2*config.minimal_distance_from_outline);
|
||||
coord_t half_max_distance = config.max_distance / 2;
|
||||
// cut lines at place where neighbor has width = min_width_for_outline_support
|
||||
// neighbor must be in direction from wide part to tiny part of island
|
||||
auto add_wide_tiny_change = [minimal_edge_length, half_max_distance,
|
||||
add_wide_tiny_change_only, &tiny_starts, &tiny_done]
|
||||
(const VoronoiGraph::Position &position, const VoronoiGraph::Node *source_node)->bool{
|
||||
if (VoronoiGraphUtils::ends_in_distanace(position, minimal_edge_length))
|
||||
return false; // no change only rich outline
|
||||
|
||||
add_wide_tiny_change_only(position);
|
||||
|
||||
coord_t support_in = position.neighbor->length() * position.ratio + half_max_distance;
|
||||
CenterStart tiny_start(position.neighbor, support_in, {source_node});
|
||||
tiny_starts.push_back(tiny_start);
|
||||
tiny_done.insert(source_node);
|
||||
return true;
|
||||
@ -1543,6 +1553,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
|
||||
|
||||
std::set<const VoronoiGraph::Node*> done;
|
||||
done.insert(wide_tiny_neighbor->node);
|
||||
|
||||
// prev node , node
|
||||
using ProcessItem = std::pair<const VoronoiGraph::Node *, const VoronoiGraph::Node *>;
|
||||
// initial proccess item from tiny part to wide part of island
|
||||
@ -1575,7 +1586,18 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
|
||||
if(add_wide_tiny_change(position, node))
|
||||
continue;
|
||||
}
|
||||
if (done.find(neighbor.node) != done.end()) continue; // loop back
|
||||
if (done.find(neighbor.node) != done.end()) continue; // loop back into field
|
||||
|
||||
// Detection that wide part do not continue over already sampled tiny part
|
||||
// Caused by histereze of wide condition.
|
||||
if (auto it = std::find_if(tiny_starts.begin(), tiny_starts.end(),
|
||||
[twin=VoronoiGraphUtils::get_twin(neighbor)](const SampleIslandUtils::CenterStart& start)->bool{
|
||||
return twin == start.neighbor; });
|
||||
it != tiny_starts.end()) {
|
||||
add_wide_tiny_change_only(VoronoiGraph::Position(&neighbor, 1.));
|
||||
tiny_starts.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (next_node == nullptr) {
|
||||
next_node = neighbor.node;
|
||||
} else {
|
||||
@ -1993,10 +2015,10 @@ void SampleIslandUtils::draw(SVG & svg,
|
||||
|
||||
void SampleIslandUtils::draw(SVG & svg,
|
||||
const SupportIslandPoints &supportIslandPoints,
|
||||
double size,
|
||||
const char * color,
|
||||
coord_t radius,
|
||||
bool write_type)
|
||||
{
|
||||
const char *color = nullptr;
|
||||
for (const auto &p : supportIslandPoints) {
|
||||
switch (p->type) {
|
||||
case SupportIslandPoint::Type::center_line1:
|
||||
@ -2016,11 +2038,11 @@ void SampleIslandUtils::draw(SVG & svg,
|
||||
case SupportIslandPoint::Type::two_points:
|
||||
default: color = "black";
|
||||
}
|
||||
svg.draw(p->point, color, size);
|
||||
svg.draw(p->point, color, radius);
|
||||
if (write_type && p->type != SupportIslandPoint::Type::undefined) {
|
||||
auto type_name = SupportIslandPoint::to_string(p->type);
|
||||
Point start = p->point + Point(size, 0.);
|
||||
svg.draw_text(start, std::string(type_name).c_str(), color);
|
||||
Point start = p->point + Point(radius, 0);
|
||||
svg.draw_text(start, std::string(type_name).c_str(), color, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,13 +267,16 @@ public:
|
||||
/// <param name="results">Result of sampling</param>
|
||||
/// <param name="lines">Source line for VD. To decide position of change from tiny to wide part</param>
|
||||
/// <param name="config">Parameters for sampling</param>
|
||||
/// <param name="is_continous">Already place sample on path</param>
|
||||
/// <returns>Wide neighbor, start of field when exists</returns>
|
||||
static std::optional<VoronoiGraph::Position> sample_center(
|
||||
CenterStarts & new_starts,
|
||||
std::set<const VoronoiGraph::Node *> &done,
|
||||
SupportIslandPoints & results,
|
||||
const Lines & lines,
|
||||
const SampleConfig & config);
|
||||
const SampleConfig & config,
|
||||
bool is_continous = false
|
||||
);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
@ -390,8 +393,7 @@ public :
|
||||
|
||||
static void draw(SVG & svg,
|
||||
const SupportIslandPoints &supportIslandPoints,
|
||||
double size,
|
||||
const char *color = "lightgreen",
|
||||
coord_t radius,
|
||||
bool write_type = true);
|
||||
};
|
||||
|
||||
|
@ -1301,20 +1301,45 @@ double VoronoiGraphUtils::outline_angle(const VoronoiGraph::Node::Neighbor &neig
|
||||
void VoronoiGraphUtils::draw(SVG & svg,
|
||||
const VoronoiGraph &graph,
|
||||
const Lines & lines,
|
||||
coord_t width,
|
||||
const SampleConfig &config,
|
||||
bool pointer_caption)
|
||||
{
|
||||
LineUtils::draw(svg, lines, "black", 0., true);
|
||||
coord_t width = config.head_radius / 10;
|
||||
LineUtils::draw(svg, lines, "black", width, false);
|
||||
|
||||
auto print_address = [&](const Point& p, const char* prefix, void * addr, const char* color){
|
||||
if (pointer_caption) {
|
||||
std::stringstream ss;
|
||||
ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr);
|
||||
std::string s = ss.str();
|
||||
svg.draw_text(p, s.c_str(), color);
|
||||
svg.draw_text(p, s.c_str(), color, 6);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<const char *> skeleton_colors{
|
||||
"yellow", // thin (min+max belowe thin)
|
||||
"yellowgreen", // on way to thin (max is above thin)
|
||||
"limegreen", // between (inside histerezis)
|
||||
"forestgreen", // on way to thick (min is belove thick)
|
||||
"darkgreen" // thick (min+max above thick)
|
||||
};
|
||||
auto get_color = [&](const VoronoiGraph::Node::Neighbor &n) {
|
||||
if (n.min_width() > config.max_width_for_center_support_line){
|
||||
return skeleton_colors[4];
|
||||
} else if (n.max_width() < config.min_width_for_outline_support){
|
||||
return skeleton_colors[0];
|
||||
} else if (n.min_width() < config.max_width_for_center_support_line &&
|
||||
n.max_width() > config.min_width_for_outline_support){
|
||||
return skeleton_colors[2];
|
||||
} else if (n.min_width() < config.min_width_for_outline_support){
|
||||
return skeleton_colors[1];
|
||||
} else if (n.max_width() > config.max_width_for_center_support_line) {
|
||||
return skeleton_colors[3];
|
||||
}
|
||||
assert(false);
|
||||
return "gray";
|
||||
};
|
||||
|
||||
for (const auto &[key, value] : graph.data) {
|
||||
Point p(key->x(), key->y());
|
||||
svg.draw(p, "lightgray", width);
|
||||
@ -1328,10 +1353,13 @@ void VoronoiGraphUtils::draw(SVG & svg,
|
||||
Point(0., 2e6));
|
||||
print_address(p, "neighbor ptr ", (void *) &n, "gray");
|
||||
if (is_second) continue;
|
||||
std::string width_str = "width min=" + std::to_string(n.min_width()) +
|
||||
" max=" + std::to_string(n.max_width());
|
||||
svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), "gray");
|
||||
draw(svg, *n.edge, lines, "gray", width);
|
||||
const char *color = get_color(n);
|
||||
if (pointer_caption) {
|
||||
std::string width_str = "width min=" + std::to_string(n.min_width()) +
|
||||
" max=" + std::to_string(n.max_width());
|
||||
svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), color, 6);
|
||||
}
|
||||
draw(svg, *n.edge, lines, color, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1358,7 +1386,8 @@ void VoronoiGraphUtils::draw(SVG & svg,
|
||||
const VoronoiGraph::Nodes &path,
|
||||
coord_t width,
|
||||
const char * color,
|
||||
bool finish)
|
||||
bool finish,
|
||||
bool caption)
|
||||
{
|
||||
const VoronoiGraph::Node *prev_node = (finish) ? path.back() : nullptr;
|
||||
int index = 0;
|
||||
@ -1372,9 +1401,10 @@ void VoronoiGraphUtils::draw(SVG & svg,
|
||||
Point from = to_point(prev_node->vertex);
|
||||
Point to = to_point(node->vertex);
|
||||
svg.draw(Line(from, to), color, width);
|
||||
|
||||
svg.draw_text(from, std::to_string(index - 1).c_str(), color);
|
||||
svg.draw_text(to, std::to_string(index).c_str(), color);
|
||||
if (caption) {
|
||||
svg.draw_text(from, std::to_string(index - 1).c_str(), color, 6);
|
||||
svg.draw_text(to, std::to_string(index).c_str(), color, 6);
|
||||
}
|
||||
prev_node = node;
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +479,7 @@ public: // draw function for debug
|
||||
static void draw(SVG & svg,
|
||||
const VoronoiGraph &graph,
|
||||
const Lines & lines,
|
||||
coord_t width,
|
||||
const SampleConfig &config,
|
||||
bool pointer_caption = false);
|
||||
static void draw(SVG & svg,
|
||||
const VD::edge_type &edge,
|
||||
@ -490,7 +490,8 @@ public: // draw function for debug
|
||||
const VoronoiGraph::Nodes &path,
|
||||
coord_t width,
|
||||
const char * color,
|
||||
bool finish = false);
|
||||
bool finish = false,
|
||||
bool caption = false);
|
||||
static void draw(SVG & svg,
|
||||
const VoronoiGraph::ExPath &path,
|
||||
coord_t width);
|
||||
|
@ -60,6 +60,8 @@ public:
|
||||
return shape.contains(p);
|
||||
});
|
||||
});
|
||||
if (it == indices.end())
|
||||
return; // no point to remove
|
||||
indices.erase(it, indices.end());
|
||||
m_tree.clear();
|
||||
m_tree.build(indices); // consume indices
|
||||
@ -235,8 +237,7 @@ void support_part_overhangs(
|
||||
Point dp = support_point.position_on_layer - p;
|
||||
if (std::abs(dp.x()) > r) return false;
|
||||
if (std::abs(dp.y()) > r) return false;
|
||||
double r2 = static_cast<double>(r);
|
||||
r2 *= r2;
|
||||
double r2 = sqr(static_cast<double>(r));
|
||||
return dp.cast<double>().squaredNorm() < r2;
|
||||
};
|
||||
|
||||
@ -246,7 +247,7 @@ void support_part_overhangs(
|
||||
near_points.add(LayerSupportPoint{
|
||||
SupportPoint{
|
||||
Vec3f{unscale<float>(p.x()), unscale<float>(p.y()), part_z},
|
||||
/* head_front_radius */ 0.4f,
|
||||
/* head_front_radius */ config.head_diameter / 2,
|
||||
SupportPointType::slope
|
||||
},
|
||||
/* position_on_layer */ p,
|
||||
@ -278,7 +279,7 @@ void support_island(const LayerPart &part, NearPoints& near_points, float part_z
|
||||
unscale<float>(sample->point.y()),
|
||||
part_z
|
||||
},
|
||||
/* head_front_radius */ 0.4f,
|
||||
/* head_front_radius */ cfg.head_diameter / 2,
|
||||
SupportPointType::island
|
||||
},
|
||||
/* position_on_layer */ sample->point,
|
||||
@ -481,12 +482,10 @@ void prepare_supports_for_layer(LayerSupportPoints &supports, float layer_z,
|
||||
/// </summary>
|
||||
/// <param name="near_points"></param>
|
||||
/// <param name="part"></param>
|
||||
void remove_supports_out_of_part(NearPoints& near_points, const LayerPart &part) {
|
||||
|
||||
// Must be greater than surface texture and lower than self supporting area
|
||||
// May be use maximal island distance
|
||||
float delta = scale_(5.);
|
||||
ExPolygons extend_shape = offset_ex(*part.shape, delta, ClipperLib::jtSquare);
|
||||
/// <param name="config"></param>
|
||||
void remove_supports_out_of_part(NearPoints& near_points, const LayerPart &part,
|
||||
const SupportPointGeneratorConfig &config) {
|
||||
ExPolygons extend_shape = offset_ex(*part.shape, config.removing_delta, ClipperLib::jtSquare);
|
||||
near_points.remove_out_of(extend_shape);
|
||||
}
|
||||
|
||||
@ -660,7 +659,7 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
|
||||
assert(layer_id != 0);
|
||||
const LayerParts &prev_layer_parts = layers[layer_id - 1].parts;
|
||||
NearPoints near_points = create_near_points(prev_layer_parts, part, prev_grids);
|
||||
remove_supports_out_of_part(near_points, part);
|
||||
remove_supports_out_of_part(near_points, part, config);
|
||||
support_part_overhangs(part, config, near_points, layer.print_z, maximal_radius);
|
||||
grids.push_back(std::move(near_points));
|
||||
}
|
||||
|
@ -51,6 +51,13 @@ struct SupportPointGeneratorConfig{
|
||||
|
||||
// Configuration for sampling island
|
||||
SampleConfig island_configuration = SampleConfigFactory::create(head_diameter);
|
||||
|
||||
// To be able support same 2d area multipletimes,
|
||||
// It is neccessary to remove support point form near KDTree structure
|
||||
|
||||
// Must be greater than surface texture and lower than self supporting area
|
||||
// May be use maximal island distance
|
||||
float removing_delta = scale_(5.);
|
||||
};
|
||||
|
||||
struct LayerPart; // forward decl.
|
||||
|
Loading…
x
Reference in New Issue
Block a user