mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-08 06:24:01 -06:00
Merge branch 'master' of github.com:prusa3d/Slic3r
This commit is contained in:
commit
4eda6e8521
6 changed files with 639 additions and 240 deletions
|
@ -185,6 +185,7 @@ public:
|
|||
bool empty() const override { return m_objects.empty(); }
|
||||
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
||||
void process() override;
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
bool is_step_done(SLAPrintObjectStep step) const;
|
||||
// Returns true if the last step was finished with success.
|
||||
bool finished() const override { return this->is_step_done(slaposIndexSlices); }
|
||||
|
|
|
@ -458,6 +458,8 @@ Polygons collect_slices_outer(const Layer &layer)
|
|||
class SupportGridPattern
|
||||
{
|
||||
public:
|
||||
// Achtung! The support_polygons need to be trimmed by trimming_polygons, otherwise
|
||||
// the selection by island_samples (see the island_samples() method) will not work!
|
||||
SupportGridPattern(
|
||||
// Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
|
||||
const Polygons &support_polygons,
|
||||
|
@ -485,6 +487,18 @@ public:
|
|||
bbox.align_to_grid(grid_resolution);
|
||||
m_grid.set_bbox(bbox);
|
||||
m_grid.create(*m_support_polygons, grid_resolution);
|
||||
#if 0
|
||||
if (m_grid.has_intersecting_edges()) {
|
||||
// EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
|
||||
m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
|
||||
m_support_polygons = &m_support_polygons_rotated;
|
||||
m_grid.set_bbox(bbox);
|
||||
m_grid.create(*m_support_polygons, grid_resolution);
|
||||
// assert(! m_grid.has_intersecting_edges());
|
||||
printf("SupportGridPattern: fixing polygons with intersection %s\n",
|
||||
m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
|
||||
}
|
||||
#endif
|
||||
m_grid.calculate_sdf();
|
||||
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
|
||||
// polygons if ever these polygons get split into parts by the trimming polygons.
|
||||
|
@ -499,9 +513,12 @@ public:
|
|||
{
|
||||
// Generate islands, so each island may be tested for overlap with m_island_samples.
|
||||
assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
|
||||
ExPolygons islands = diff_ex(
|
||||
m_grid.contours_simplified(offset_in_grid, fill_holes),
|
||||
*m_trimming_polygons, false);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
|
||||
ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
|
||||
#else
|
||||
ExPolygons islands = diff_ex(m_grid.contours_simplified(offset_in_grid, fill_holes), *m_trimming_polygons, false);
|
||||
#endif
|
||||
|
||||
// Extract polygons, which contain some of the m_island_samples.
|
||||
Polygons out;
|
||||
|
@ -551,7 +568,10 @@ public:
|
|||
bbox.merge(get_extents(islands));
|
||||
if (!out.empty())
|
||||
bbox.merge(get_extents(out));
|
||||
if (!support_polygons_simplified.empty())
|
||||
bbox.merge(get_extents(support_polygons_simplified));
|
||||
SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox);
|
||||
svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
|
||||
svg.draw(islands, "red", 0.5f);
|
||||
svg.draw(union_ex(out), "green", 0.5f);
|
||||
svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
|
||||
|
@ -568,7 +588,121 @@ public:
|
|||
return out;
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
void serialize(const std::string &path)
|
||||
{
|
||||
FILE *file = ::fopen(path.c_str(), "wb");
|
||||
::fwrite(&m_support_spacing, 8, 1, file);
|
||||
::fwrite(&m_support_angle, 8, 1, file);
|
||||
uint32_t n_polygons = m_support_polygons->size();
|
||||
::fwrite(&n_polygons, 4, 1, file);
|
||||
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||
const Polygon &poly = (*m_support_polygons)[i];
|
||||
uint32_t n_points = poly.size();
|
||||
::fwrite(&n_points, 4, 1, file);
|
||||
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||
const Point &pt = poly.points[j];
|
||||
::fwrite(&pt.x, sizeof(coord_t), 1, file);
|
||||
::fwrite(&pt.y, sizeof(coord_t), 1, file);
|
||||
}
|
||||
}
|
||||
n_polygons = m_trimming_polygons->size();
|
||||
::fwrite(&n_polygons, 4, 1, file);
|
||||
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||
const Polygon &poly = (*m_trimming_polygons)[i];
|
||||
uint32_t n_points = poly.size();
|
||||
::fwrite(&n_points, 4, 1, file);
|
||||
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||
const Point &pt = poly.points[j];
|
||||
::fwrite(&pt.x, sizeof(coord_t), 1, file);
|
||||
::fwrite(&pt.y, sizeof(coord_t), 1, file);
|
||||
}
|
||||
}
|
||||
::fclose(file);
|
||||
}
|
||||
|
||||
static SupportGridPattern deserialize(const std::string &path, int which = -1)
|
||||
{
|
||||
SupportGridPattern out;
|
||||
out.deserialize_(path, which);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Deserialization constructor
|
||||
bool deserialize_(const std::string &path, int which = -1)
|
||||
{
|
||||
FILE *file = ::fopen(path.c_str(), "rb");
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
m_support_polygons = &m_support_polygons_deserialized;
|
||||
m_trimming_polygons = &m_trimming_polygons_deserialized;
|
||||
|
||||
::fread(&m_support_spacing, 8, 1, file);
|
||||
::fread(&m_support_angle, 8, 1, file);
|
||||
//FIXME
|
||||
//m_support_spacing *= 0.01 / 2;
|
||||
uint32_t n_polygons;
|
||||
::fread(&n_polygons, 4, 1, file);
|
||||
m_support_polygons_deserialized.reserve(n_polygons);
|
||||
int32_t scale = 1;
|
||||
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||
Polygon poly;
|
||||
uint32_t n_points;
|
||||
::fread(&n_points, 4, 1, file);
|
||||
poly.points.reserve(n_points);
|
||||
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||
coord_t x, y;
|
||||
::fread(&x, sizeof(coord_t), 1, file);
|
||||
::fread(&y, sizeof(coord_t), 1, file);
|
||||
poly.points.emplace_back(Point(x * scale, y * scale));
|
||||
}
|
||||
if (which == -1 || which == i)
|
||||
m_support_polygons_deserialized.emplace_back(std::move(poly));
|
||||
printf("Polygon %d, area: %lf\n", i, area(poly.points));
|
||||
}
|
||||
::fread(&n_polygons, 4, 1, file);
|
||||
m_trimming_polygons_deserialized.reserve(n_polygons);
|
||||
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||
Polygon poly;
|
||||
uint32_t n_points;
|
||||
::fread(&n_points, 4, 1, file);
|
||||
poly.points.reserve(n_points);
|
||||
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||
coord_t x, y;
|
||||
::fread(&x, sizeof(coord_t), 1, file);
|
||||
::fread(&y, sizeof(coord_t), 1, file);
|
||||
poly.points.emplace_back(Point(x * scale, y * scale));
|
||||
}
|
||||
m_trimming_polygons_deserialized.emplace_back(std::move(poly));
|
||||
}
|
||||
::fclose(file);
|
||||
|
||||
m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false);
|
||||
//m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false));
|
||||
|
||||
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
|
||||
coord_t grid_resolution = coord_t(scale_(m_support_spacing));
|
||||
BoundingBox bbox = get_extents(*m_support_polygons);
|
||||
bbox.offset(20);
|
||||
bbox.align_to_grid(grid_resolution);
|
||||
m_grid.set_bbox(bbox);
|
||||
m_grid.create(*m_support_polygons, grid_resolution);
|
||||
m_grid.calculate_sdf();
|
||||
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
|
||||
// polygons if ever these polygons get split into parts by the trimming polygons.
|
||||
m_island_samples = island_samples(*m_support_polygons);
|
||||
return true;
|
||||
}
|
||||
|
||||
const Polygons& support_polygons() const { return *m_support_polygons; }
|
||||
const Polygons& trimming_polygons() const { return *m_trimming_polygons; }
|
||||
const EdgeGrid::Grid& grid() const { return m_grid; }
|
||||
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
private:
|
||||
SupportGridPattern() {}
|
||||
SupportGridPattern& operator=(const SupportGridPattern &rhs);
|
||||
|
||||
#if 0
|
||||
|
@ -639,6 +773,12 @@ private:
|
|||
// Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
|
||||
// to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
|
||||
Points m_island_samples;
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
// support for deserialization of m_support_polygons, m_trimming_polygons
|
||||
Polygons m_support_polygons_deserialized;
|
||||
Polygons m_trimming_polygons_deserialized;
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
};
|
||||
|
||||
namespace SupportMaterialInternal {
|
||||
|
@ -783,17 +923,40 @@ namespace SupportMaterialInternal {
|
|||
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
|
||||
polygons_append(bridges, surface.expolygon);
|
||||
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
|
||||
contact_polygons = diff(contact_polygons, bridges, true);
|
||||
// Add the bridge anchors into the region.
|
||||
// Remove the unsupported ends of the bridges from the bridged areas.
|
||||
//FIXME add supports at regular intervals to support long bridges!
|
||||
polygons_append(contact_polygons,
|
||||
intersection(
|
||||
bridges = diff(bridges,
|
||||
// Offset unsupported edges into polygons.
|
||||
offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
|
||||
bridges));
|
||||
offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
// Remove bridged areas from the supported areas.
|
||||
contact_polygons = diff(contact_polygons, bridges, true);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int Test()
|
||||
{
|
||||
// for (int i = 0; i < 30; ++ i)
|
||||
{
|
||||
int i = -1;
|
||||
// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000-prev.bin", i);
|
||||
// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000.bin", i);
|
||||
auto grid = SupportGridPattern::deserialize("d:\\temp\\support-top-contacts-final-run1-layer27-z5.650000.bin", i);
|
||||
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = grid.grid().intersecting_edges();
|
||||
if (! intersections.empty())
|
||||
printf("Intersections between contours!\n");
|
||||
Slic3r::export_intersections_to_svg("d:\\temp\\support_polygon_intersections.svg", grid.support_polygons());
|
||||
Slic3r::SVG::export_expolygons("d:\\temp\\support_polygons.svg", union_ex(grid.support_polygons(), false));
|
||||
Slic3r::SVG::export_expolygons("d:\\temp\\trimming_polygons.svg", union_ex(grid.trimming_polygons(), false));
|
||||
Polygons extracted = grid.extract_support(scale_(0.21 / 2), true);
|
||||
Slic3r::SVG::export_expolygons("d:\\temp\\extracted.svg", union_ex(extracted, false));
|
||||
printf("hu!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int run_support_test = Test();
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
// Generate top contact layers supporting overhangs.
|
||||
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
||||
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
||||
|
@ -1096,6 +1259,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
}
|
||||
}
|
||||
|
||||
// Achtung! The contact_polygons need to be trimmed by slices_margin_cached, otherwise
|
||||
// the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work!
|
||||
SupportGridPattern support_grid_pattern(
|
||||
// Support islands, to be stretched into a grid.
|
||||
contact_polygons,
|
||||
|
@ -1114,9 +1279,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
|
||||
Polygons dense_interface_polygons = diff(overhang_polygons,
|
||||
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
if (! dense_interface_polygons.empty()) {
|
||||
//FIXME do it for the bridges only?
|
||||
dense_interface_polygons =
|
||||
// Achtung! The dense_interface_polygons need to be trimmed by slices_margin_cached, otherwise
|
||||
// the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work!
|
||||
diff(
|
||||
// Regularize the contour.
|
||||
offset(dense_interface_polygons, no_interface_offset * 0.1f),
|
||||
slices_margin_cached);
|
||||
SupportGridPattern support_grid_pattern(
|
||||
// Support islands, to be stretched into a grid.
|
||||
dense_interface_polygons,
|
||||
|
@ -1126,8 +1296,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
|
||||
Geometry::deg2rad(m_object_config->support_material_angle.value));
|
||||
new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
{
|
||||
support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z));
|
||||
|
||||
BoundingBox bbox = get_extents(contact_polygons);
|
||||
bbox.merge(get_extents(new_layer.polygons));
|
||||
::Slic3r::SVG svg(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z));
|
||||
svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f);
|
||||
svg.draw(union_ex(contact_polygons, false), "blue", 0.5f);
|
||||
svg.draw(union_ex(dense_interface_polygons, false), "green", 0.5f);
|
||||
svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f);
|
||||
svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f));
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
}
|
||||
}
|
||||
#ifdef SLIC3R_DEBUG
|
||||
{
|
||||
BoundingBox bbox = get_extents(contact_polygons);
|
||||
bbox.merge(get_extents(new_layer.polygons));
|
||||
::Slic3r::SVG svg(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z));
|
||||
svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f);
|
||||
svg.draw(union_ex(contact_polygons, false), "blue", 0.5f);
|
||||
svg.draw(union_ex(overhang_polygons, false), "green", 0.5f);
|
||||
svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f);
|
||||
svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f));
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
|
||||
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
|
||||
|
|
|
@ -1212,6 +1212,345 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct OpenPolyline {
|
||||
OpenPolyline() {};
|
||||
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
|
||||
start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); }
|
||||
void reverse() {
|
||||
std::swap(start, end);
|
||||
std::reverse(points.begin(), points.end());
|
||||
}
|
||||
IntersectionReference start;
|
||||
IntersectionReference end;
|
||||
Points points;
|
||||
double length;
|
||||
bool consumed;
|
||||
};
|
||||
|
||||
// called by TriangleMeshSlicer::make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
|
||||
// Only connects segments crossing triangles of the same orientation.
|
||||
static void chain_lines_by_triangle_connectivity(std::vector<IntersectionLine> &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
|
||||
{
|
||||
// Build a map of lines by edge_a_id and a_id.
|
||||
std::vector<IntersectionLine*> by_edge_a_id;
|
||||
std::vector<IntersectionLine*> by_a_id;
|
||||
by_edge_a_id.reserve(lines.size());
|
||||
by_a_id.reserve(lines.size());
|
||||
for (IntersectionLine &line : lines) {
|
||||
if (! line.skip()) {
|
||||
if (line.edge_a_id != -1)
|
||||
by_edge_a_id.emplace_back(&line);
|
||||
if (line.a_id != -1)
|
||||
by_a_id.emplace_back(&line);
|
||||
}
|
||||
}
|
||||
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
|
||||
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
|
||||
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
|
||||
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
|
||||
// Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
|
||||
IntersectionLines::iterator it_line_seed = lines.begin();
|
||||
for (;;) {
|
||||
// take first spare line and start a new loop
|
||||
IntersectionLine *first_line = nullptr;
|
||||
for (; it_line_seed != lines.end(); ++ it_line_seed)
|
||||
if (it_line_seed->is_seed_candidate()) {
|
||||
//if (! it_line_seed->skip()) {
|
||||
first_line = &(*it_line_seed ++);
|
||||
break;
|
||||
}
|
||||
if (first_line == nullptr)
|
||||
break;
|
||||
first_line->set_skip();
|
||||
Points loop_pts;
|
||||
loop_pts.emplace_back(first_line->a);
|
||||
IntersectionLine *last_line = first_line;
|
||||
|
||||
/*
|
||||
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
|
||||
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
|
||||
*/
|
||||
|
||||
IntersectionLine key;
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
IntersectionLine* next_line = nullptr;
|
||||
if (last_line->edge_b_id != -1) {
|
||||
key.edge_a_id = last_line->edge_b_id;
|
||||
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
|
||||
if (it_begin != by_edge_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip()) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr && last_line->b_id != -1) {
|
||||
key.a_id = last_line->b_id;
|
||||
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
|
||||
if (it_begin != by_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip()) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr) {
|
||||
// Check whether we closed this loop.
|
||||
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
loops.emplace_back(std::move(loop_pts));
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||
#endif
|
||||
} else {
|
||||
// This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
|
||||
loop_pts.emplace_back(last_line->b);
|
||||
open_polylines.emplace_back(OpenPolyline(
|
||||
IntersectionReference(first_line->a_id, first_line->edge_a_id),
|
||||
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
|
||||
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
|
||||
*/
|
||||
loop_pts.emplace_back(next_line->a);
|
||||
last_line = next_line;
|
||||
next_line->set_skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<OpenPolyline*> open_polylines_sorted(std::vector<OpenPolyline> &open_polylines, bool update_lengths)
|
||||
{
|
||||
std::vector<OpenPolyline*> out;
|
||||
out.reserve(open_polylines.size());
|
||||
for (OpenPolyline &opl : open_polylines)
|
||||
if (! opl.consumed) {
|
||||
if (update_lengths)
|
||||
opl.length = Slic3r::length(opl.points);
|
||||
out.emplace_back(&opl);
|
||||
}
|
||||
std::sort(out.begin(), out.end(), [](const OpenPolyline *lhs, const OpenPolyline *rhs){ return lhs->length > rhs->length; });
|
||||
return out;
|
||||
}
|
||||
|
||||
// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices.
|
||||
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
||||
static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines, Polygons &loops, bool try_connect_reversed)
|
||||
{
|
||||
// Store the end points of open_polylines into vectors sorted
|
||||
struct OpenPolylineEnd {
|
||||
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
||||
OpenPolyline *polyline;
|
||||
// Is it the start or end point?
|
||||
bool start;
|
||||
const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
|
||||
// Return a unique ID for the intersection point.
|
||||
// Return a positive id for a point, or a negative id for an edge.
|
||||
int id() const { const IntersectionReference &r = ipref(); return (r.point_id >= 0) ? r.point_id : - r.edge_id; }
|
||||
bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; }
|
||||
};
|
||||
auto by_id_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.id() < ope2.id(); };
|
||||
std::vector<OpenPolylineEnd> by_id;
|
||||
by_id.reserve(2 * open_polylines.size());
|
||||
for (OpenPolyline &opl : open_polylines) {
|
||||
if (opl.start.point_id != -1 || opl.start.edge_id != -1)
|
||||
by_id.emplace_back(OpenPolylineEnd(&opl, true));
|
||||
if (try_connect_reversed && (opl.end.point_id != -1 || opl.end.edge_id != -1))
|
||||
by_id.emplace_back(OpenPolylineEnd(&opl, false));
|
||||
}
|
||||
std::sort(by_id.begin(), by_id.end(), by_id_lower);
|
||||
// Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute).
|
||||
auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> std::vector<OpenPolylineEnd>::iterator {
|
||||
for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
|
||||
it != by_id.end() && it->id() == end.id(); ++ it)
|
||||
if (*it == end)
|
||||
return it;
|
||||
return by_id.end();
|
||||
};
|
||||
// Try to connect the loops.
|
||||
std::vector<OpenPolyline*> sorted_by_length = open_polylines_sorted(open_polylines, false);
|
||||
for (OpenPolyline *opl : sorted_by_length) {
|
||||
if (opl->consumed)
|
||||
continue;
|
||||
opl->consumed = true;
|
||||
OpenPolylineEnd end(opl, false);
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
auto it_next_start = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
|
||||
for (; it_next_start != by_id.end() && it_next_start->id() == end.id(); ++ it_next_start)
|
||||
if (! it_next_start->polyline->consumed)
|
||||
goto found;
|
||||
// The current loop could not be closed. Unmark the segment.
|
||||
opl->consumed = false;
|
||||
break;
|
||||
found:
|
||||
// Attach this polyline to the end of the initial polyline.
|
||||
if (it_next_start->start) {
|
||||
auto it = it_next_start->polyline->points.begin();
|
||||
std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points));
|
||||
} else {
|
||||
auto it = it_next_start->polyline->points.rbegin();
|
||||
std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points));
|
||||
}
|
||||
opl->length += it_next_start->polyline->length;
|
||||
// Mark the next polyline as consumed.
|
||||
it_next_start->polyline->points.clear();
|
||||
it_next_start->polyline->length = 0.;
|
||||
it_next_start->polyline->consumed = true;
|
||||
if (try_connect_reversed) {
|
||||
// Running in a mode, where the polylines may be connected by mixing their orientations.
|
||||
// Update the end point lookup structure after the end point of the current polyline was extended.
|
||||
auto it_end = find_polyline_end(end);
|
||||
auto it_next_end = find_polyline_end(OpenPolylineEnd(it_next_start->polyline, !it_next_start->start));
|
||||
// Swap the end points of the current and next polyline, but keep the polyline ptr and the start flag.
|
||||
std::swap(opl->end, it_next_end->start ? it_next_end->polyline->start : it_next_end->polyline->end);
|
||||
// Swap the positions of OpenPolylineEnd structures in the sorted array to match their respective end point positions.
|
||||
std::swap(*it_end, *it_next_end);
|
||||
}
|
||||
// Check whether we closed this loop.
|
||||
if ((opl->start.edge_id != -1 && opl->start.edge_id == opl->end.edge_id) ||
|
||||
(opl->start.point_id != -1 && opl->start.point_id == opl->end.point_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
//assert(opl->points.front().point_id == opl->points.back().point_id);
|
||||
//assert(opl->points.front().edge_id == opl->points.back().edge_id);
|
||||
// Remove the duplicate last point.
|
||||
opl->points.pop_back();
|
||||
if (opl->points.size() >= 3) {
|
||||
if (try_connect_reversed && area(opl->points) < 0)
|
||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||
// the orientation of the patched up polygon is not known.
|
||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||
std::reverse(opl->points.begin(), opl->points.end());
|
||||
loops.emplace_back(std::move(opl->points));
|
||||
}
|
||||
opl->points.clear();
|
||||
break;
|
||||
}
|
||||
// Continue with the current loop.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices,
|
||||
// possibly closing small gaps.
|
||||
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
||||
static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed)
|
||||
{
|
||||
const coord_t max_gap_scaled = (coord_t)scale_(max_gap);
|
||||
|
||||
// Sort the open polylines by their length, so the new loops will be seeded from longer chains.
|
||||
// Update the polyline lengths, return only not yet consumed polylines.
|
||||
std::vector<OpenPolyline*> sorted_by_length = open_polylines_sorted(open_polylines, true);
|
||||
|
||||
// Store the end points of open_polylines into ClosestPointInRadiusLookup<OpenPolylineEnd>.
|
||||
struct OpenPolylineEnd {
|
||||
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
||||
OpenPolyline *polyline;
|
||||
// Is it the start or end point?
|
||||
bool start;
|
||||
const Point& point() const { return start ? polyline->points.front() : polyline->points.back(); }
|
||||
bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; }
|
||||
};
|
||||
struct OpenPolylineEndAccessor {
|
||||
const Point* operator()(const OpenPolylineEnd &pt) const { return pt.polyline->consumed ? nullptr : &pt.point(); }
|
||||
};
|
||||
typedef ClosestPointInRadiusLookup<OpenPolylineEnd, OpenPolylineEndAccessor> ClosestPointLookupType;
|
||||
ClosestPointLookupType closest_end_point_lookup(max_gap_scaled);
|
||||
for (OpenPolyline *opl : sorted_by_length) {
|
||||
closest_end_point_lookup.insert(OpenPolylineEnd(opl, true));
|
||||
if (try_connect_reversed)
|
||||
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
|
||||
}
|
||||
// Try to connect the loops.
|
||||
for (OpenPolyline *opl : sorted_by_length) {
|
||||
if (opl->consumed)
|
||||
continue;
|
||||
OpenPolylineEnd end(opl, false);
|
||||
if (try_connect_reversed)
|
||||
// The end point of this polyline will be modified, thus the following entry will become invalid. Remove it.
|
||||
closest_end_point_lookup.erase(end);
|
||||
opl->consumed = true;
|
||||
size_t n_segments_joined = 1;
|
||||
for (;;) {
|
||||
// Find a line starting where last one finishes, only return non-consumed open polylines (OpenPolylineEndAccessor returns null for consumed).
|
||||
std::pair<const OpenPolylineEnd*, double> next_start_and_dist = closest_end_point_lookup.find(end.point());
|
||||
const OpenPolylineEnd *next_start = next_start_and_dist.first;
|
||||
// Check whether we closed this loop.
|
||||
double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast<double>().squaredNorm();
|
||||
bool loop_closed = current_loop_closing_distance2 < coordf_t(max_gap_scaled) * coordf_t(max_gap_scaled);
|
||||
if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) {
|
||||
// Heuristics to decide, whether to close the loop, or connect another polyline.
|
||||
// One should avoid closing loops shorter than max_gap_scaled.
|
||||
loop_closed = sqrt(current_loop_closing_distance2) < 0.3 * length(opl->points);
|
||||
}
|
||||
if (loop_closed) {
|
||||
// Remove the start point of the current polyline from the lookup.
|
||||
// Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail.
|
||||
opl->consumed = false;
|
||||
closest_end_point_lookup.erase(OpenPolylineEnd(opl, true));
|
||||
if (current_loop_closing_distance2 == 0.) {
|
||||
// Remove the duplicate last point.
|
||||
opl->points.pop_back();
|
||||
} else {
|
||||
// The end points are different, keep both of them.
|
||||
}
|
||||
if (opl->points.size() >= 3) {
|
||||
if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0)
|
||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||
// the orientation of the patched up polygon is not known.
|
||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||
std::reverse(opl->points.begin(), opl->points.end());
|
||||
loops.emplace_back(std::move(opl->points));
|
||||
}
|
||||
opl->points.clear();
|
||||
opl->consumed = true;
|
||||
break;
|
||||
}
|
||||
if (next_start == nullptr) {
|
||||
// The current loop could not be closed. Unmark the segment.
|
||||
opl->consumed = false;
|
||||
if (try_connect_reversed)
|
||||
// Re-insert the end point.
|
||||
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
|
||||
break;
|
||||
}
|
||||
// Attach this polyline to the end of the initial polyline.
|
||||
if (next_start->start) {
|
||||
auto it = next_start->polyline->points.begin();
|
||||
if (*it == opl->points.back())
|
||||
++ it;
|
||||
std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points));
|
||||
} else {
|
||||
auto it = next_start->polyline->points.rbegin();
|
||||
if (*it == opl->points.back())
|
||||
++ it;
|
||||
std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points));
|
||||
}
|
||||
++ n_segments_joined;
|
||||
// Remove the end points of the consumed polyline segment from the lookup.
|
||||
OpenPolyline *opl2 = next_start->polyline;
|
||||
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true));
|
||||
if (try_connect_reversed)
|
||||
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false));
|
||||
opl2->points.clear();
|
||||
opl2->consumed = true;
|
||||
// Continue with the current loop.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
||||
{
|
||||
#if 0
|
||||
|
@ -1221,231 +1560,83 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||
assert(l.a != l.b);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
remove_tangent_edges(lines);
|
||||
// There should be no tangent edges, as the horizontal triangles are ignored and if two triangles touch at a cutting plane,
|
||||
// only the bottom triangle is considered to be cutting the plane.
|
||||
// remove_tangent_edges(lines);
|
||||
|
||||
struct OpenPolyline {
|
||||
OpenPolyline() {};
|
||||
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
|
||||
start(start), end(end), points(std::move(points)), consumed(false) {}
|
||||
void reverse() {
|
||||
std::swap(start, end);
|
||||
std::reverse(points.begin(), points.end());
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
BoundingBox bbox_svg;
|
||||
{
|
||||
static int iRun = 0;
|
||||
for (const Line &line : lines) {
|
||||
bbox_svg.merge(line.a);
|
||||
bbox_svg.merge(line.b);
|
||||
}
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-raw_lines-%d.svg", iRun ++).c_str(), bbox_svg);
|
||||
for (const Line &line : lines)
|
||||
svg.draw(line);
|
||||
svg.Close();
|
||||
}
|
||||
IntersectionReference start;
|
||||
IntersectionReference end;
|
||||
Points points;
|
||||
bool consumed;
|
||||
};
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
std::vector<OpenPolyline> open_polylines;
|
||||
{
|
||||
// Build a map of lines by edge_a_id and a_id.
|
||||
std::vector<IntersectionLine*> by_edge_a_id;
|
||||
std::vector<IntersectionLine*> by_a_id;
|
||||
by_edge_a_id.reserve(lines.size());
|
||||
by_a_id.reserve(lines.size());
|
||||
for (IntersectionLine &line : lines) {
|
||||
if (! line.skip()) {
|
||||
if (line.edge_a_id != -1)
|
||||
by_edge_a_id.emplace_back(&line);
|
||||
if (line.a_id != -1)
|
||||
by_a_id.emplace_back(&line);
|
||||
}
|
||||
chain_lines_by_triangle_connectivity(lines, *loops, open_polylines);
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
|
||||
svg.draw(union_ex(*loops));
|
||||
for (const OpenPolyline &pl : open_polylines)
|
||||
svg.draw(Polyline(pl.points), "red");
|
||||
svg.Close();
|
||||
}
|
||||
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
|
||||
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
|
||||
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
|
||||
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
|
||||
// Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
|
||||
IntersectionLines::iterator it_line_seed = lines.begin();
|
||||
for (;;) {
|
||||
// take first spare line and start a new loop
|
||||
IntersectionLine *first_line = nullptr;
|
||||
for (; it_line_seed != lines.end(); ++ it_line_seed)
|
||||
if (it_line_seed->is_seed_candidate()) {
|
||||
//if (! it_line_seed->skip()) {
|
||||
first_line = &(*it_line_seed ++);
|
||||
break;
|
||||
}
|
||||
if (first_line == nullptr)
|
||||
break;
|
||||
first_line->set_skip();
|
||||
Points loop_pts;
|
||||
loop_pts.emplace_back(first_line->a);
|
||||
IntersectionLine *last_line = first_line;
|
||||
|
||||
/*
|
||||
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
|
||||
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
|
||||
*/
|
||||
|
||||
IntersectionLine key;
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
IntersectionLine* next_line = nullptr;
|
||||
if (last_line->edge_b_id != -1) {
|
||||
key.edge_a_id = last_line->edge_b_id;
|
||||
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
|
||||
if (it_begin != by_edge_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip()) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr && last_line->b_id != -1) {
|
||||
key.a_id = last_line->b_id;
|
||||
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
|
||||
if (it_begin != by_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip()) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr) {
|
||||
// Check whether we closed this loop.
|
||||
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
loops->emplace_back(std::move(loop_pts));
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||
#endif
|
||||
} else {
|
||||
// This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
|
||||
loop_pts.emplace_back(last_line->b);
|
||||
open_polylines.emplace_back(OpenPolyline(
|
||||
IntersectionReference(first_line->a_id, first_line->edge_a_id),
|
||||
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
|
||||
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
|
||||
*/
|
||||
loop_pts.emplace_back(next_line->a);
|
||||
last_line = next_line;
|
||||
next_line->set_skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// Now process the open polylines.
|
||||
if (! open_polylines.empty()) {
|
||||
// Store the end points of open_polylines into vectors sorted
|
||||
struct OpenPolylineEnd {
|
||||
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
||||
OpenPolyline *polyline;
|
||||
// Is it the start or end point?
|
||||
bool start;
|
||||
const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
|
||||
int point_id() const { return ipref().point_id; }
|
||||
int edge_id () const { return ipref().edge_id; }
|
||||
};
|
||||
auto by_edge_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.edge_id() < ope2.edge_id(); };
|
||||
auto by_point_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.point_id() < ope2.point_id(); };
|
||||
std::vector<OpenPolylineEnd> by_edge_id;
|
||||
std::vector<OpenPolylineEnd> by_point_id;
|
||||
by_edge_id.reserve(2 * open_polylines.size());
|
||||
by_point_id.reserve(2 * open_polylines.size());
|
||||
for (OpenPolyline &opl : open_polylines) {
|
||||
if (opl.start.edge_id != -1)
|
||||
by_edge_id .emplace_back(OpenPolylineEnd(&opl, true));
|
||||
if (opl.end.edge_id != -1)
|
||||
by_edge_id .emplace_back(OpenPolylineEnd(&opl, false));
|
||||
if (opl.start.point_id != -1)
|
||||
by_point_id.emplace_back(OpenPolylineEnd(&opl, true));
|
||||
if (opl.end.point_id != -1)
|
||||
by_point_id.emplace_back(OpenPolylineEnd(&opl, false));
|
||||
}
|
||||
std::sort(by_edge_id .begin(), by_edge_id .end(), by_edge_lower);
|
||||
std::sort(by_point_id.begin(), by_point_id.end(), by_point_lower);
|
||||
// Do it in two rounds, first try to connect in the same direction only,
|
||||
// then try to connect the open polylines in reversed order as well.
|
||||
chain_open_polylines_exact(open_polylines, *loops, false);
|
||||
chain_open_polylines_exact(open_polylines, *loops, true);
|
||||
|
||||
// Try to connect the loops.
|
||||
for (OpenPolyline &opl : open_polylines) {
|
||||
if (opl.consumed)
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
|
||||
svg.draw(union_ex(*loops));
|
||||
for (const OpenPolyline &pl : open_polylines) {
|
||||
if (pl.points.empty())
|
||||
continue;
|
||||
opl.consumed = true;
|
||||
OpenPolylineEnd end(&opl, false);
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
OpenPolylineEnd* next_start = nullptr;
|
||||
if (end.edge_id() != -1) {
|
||||
auto it_begin = std::lower_bound(by_edge_id.begin(), by_edge_id.end(), end, by_edge_lower);
|
||||
if (it_begin != by_edge_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_edge_id.end(), end, by_edge_lower);
|
||||
for (auto it_edge = it_begin; it_edge != it_end; ++ it_edge)
|
||||
if (! it_edge->polyline->consumed) {
|
||||
next_start = &(*it_edge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_start == nullptr && end.point_id() != -1) {
|
||||
auto it_begin = std::lower_bound(by_point_id.begin(), by_point_id.end(), end, by_point_lower);
|
||||
if (it_begin != by_point_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_point_id.end(), end, by_point_lower);
|
||||
for (auto it_point = it_begin; it_point != it_end; ++ it_point)
|
||||
if (! it_point->polyline->consumed) {
|
||||
next_start = &(*it_point);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_start == nullptr) {
|
||||
// The current loop could not be closed. Unmark the segment.
|
||||
opl.consumed = false;
|
||||
break;
|
||||
}
|
||||
// Attach this polyline to the end of the initial polyline.
|
||||
if (next_start->start) {
|
||||
auto it = next_start->polyline->points.begin();
|
||||
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
|
||||
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.end());
|
||||
} else {
|
||||
auto it = next_start->polyline->points.rbegin();
|
||||
std::copy(++ it, next_start->polyline->points.rend(), back_inserter(opl.points));
|
||||
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.rend());
|
||||
}
|
||||
end = *next_start;
|
||||
end.start = !end.start;
|
||||
next_start->polyline->points.clear();
|
||||
next_start->polyline->consumed = true;
|
||||
// Check whether we closed this loop.
|
||||
const IntersectionReference &ip1 = opl.start;
|
||||
const IntersectionReference &ip2 = end.ipref();
|
||||
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
|
||||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
//assert(opl.points.front().point_id == opl.points.back().point_id);
|
||||
//assert(opl.points.front().edge_id == opl.points.back().edge_id);
|
||||
// Remove the duplicate last point.
|
||||
opl.points.pop_back();
|
||||
if (opl.points.size() >= 3) {
|
||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||
// the orientation of the patched up polygon is not known.
|
||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||
double area = 0.;
|
||||
for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++)
|
||||
area += double(opl.points[j](0) + opl.points[i](0)) * double(opl.points[i](1) - opl.points[j](1));
|
||||
if (area < 0)
|
||||
std::reverse(opl.points.begin(), opl.points.end());
|
||||
loops->emplace_back(std::move(opl.points));
|
||||
}
|
||||
opl.points.clear();
|
||||
break;
|
||||
}
|
||||
// Continue with the current loop.
|
||||
}
|
||||
svg.draw(Polyline(pl.points), "red");
|
||||
svg.draw(pl.points.front(), "blue");
|
||||
svg.draw(pl.points.back(), "blue");
|
||||
}
|
||||
svg.Close();
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// Try to close gaps.
|
||||
// Do it in two rounds, first try to connect in the same direction only,
|
||||
// then try to connect the open polylines in reversed order as well.
|
||||
const double max_gap = 2.; //mm
|
||||
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
|
||||
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
|
||||
svg.draw(union_ex(*loops));
|
||||
for (const OpenPolyline &pl : open_polylines) {
|
||||
if (pl.points.empty())
|
||||
continue;
|
||||
svg.draw(Polyline(pl.points), "red");
|
||||
svg.draw(pl.points.front(), "blue");
|
||||
svg.draw(pl.points.back(), "blue");
|
||||
}
|
||||
svg.Close();
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
}
|
||||
|
||||
// Only used to cut the mesh into two halves.
|
||||
|
@ -1580,10 +1771,11 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
|||
// p_slices = diff(p_slices, *loop);
|
||||
//}
|
||||
|
||||
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||
double safety_offset = scale_(0.0499);
|
||||
//FIXME see https://github.com/prusa3d/Slic3r/issues/520
|
||||
// double safety_offset = scale_(0.0001);
|
||||
// Perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||
// 0.0499 comes from https://github.com/slic3r/Slic3r/issues/959
|
||||
// double safety_offset = scale_(0.0499);
|
||||
// 0.0001 is set to satisfy GH #520, #1029, #1364
|
||||
double safety_offset = scale_(0.0001);
|
||||
|
||||
/* The following line is commented out because it can generate wrong polygons,
|
||||
see for example issue #661 */
|
||||
|
|
|
@ -68,8 +68,10 @@ static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
|
|||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
static const float DEFAULT_BG_COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
|
||||
static const float ERROR_BG_COLOR[3] = { 144.0f / 255.0f, 49.0f / 255.0f, 10.0f / 255.0f };
|
||||
static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f };
|
||||
static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f };
|
||||
static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f };
|
||||
static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f };
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
@ -5810,14 +5812,18 @@ void GLCanvas3D::_render_background() const
|
|||
::glDisable(GL_DEPTH_TEST);
|
||||
|
||||
::glBegin(GL_QUADS);
|
||||
::glColor3f(0.0f, 0.0f, 0.0f);
|
||||
if (m_dynamic_background_enabled && _is_any_volume_outside())
|
||||
::glColor3fv(ERROR_BG_DARK_COLOR);
|
||||
else
|
||||
::glColor3fv(DEFAULT_BG_DARK_COLOR);
|
||||
|
||||
::glVertex2f(-1.0f, -1.0f);
|
||||
::glVertex2f(1.0f, -1.0f);
|
||||
|
||||
if (m_dynamic_background_enabled && _is_any_volume_outside())
|
||||
::glColor3fv(ERROR_BG_COLOR);
|
||||
::glColor3fv(ERROR_BG_LIGHT_COLOR);
|
||||
else
|
||||
::glColor3fv(DEFAULT_BG_COLOR);
|
||||
::glColor3fv(DEFAULT_BG_LIGHT_COLOR);
|
||||
|
||||
::glVertex2f(1.0f, 1.0f);
|
||||
::glVertex2f(-1.0f, 1.0f);
|
||||
|
|
|
@ -337,7 +337,7 @@ void ObjectList::selection_changed()
|
|||
|
||||
void ObjectList::OnChar(wxKeyEvent& event)
|
||||
{
|
||||
printf("KeyDown event\n");
|
||||
// printf("KeyDown event\n");
|
||||
if (event.GetKeyCode() == WXK_BACK){
|
||||
printf("WXK_BACK\n");
|
||||
remove();
|
||||
|
@ -427,10 +427,10 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||
|
||||
void ObjectList::OnBeginDrag(wxDataViewEvent &event)
|
||||
{
|
||||
wxDataViewItem item(event.GetItem());
|
||||
const wxDataViewItem item(event.GetItem());
|
||||
|
||||
// only allow drags for item, not containers
|
||||
if (multiple_selection() ||
|
||||
if (multiple_selection() || GetSelection()!=item ||
|
||||
m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||
m_objects_model->GetItemType(item) != itVolume ) {
|
||||
event.Veto();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue