Merge branch 'master' into dev

This commit is contained in:
bubnikv 2019-09-13 16:19:35 +02:00
commit a9182fb0b3
11 changed files with 292 additions and 51 deletions

View file

@ -537,6 +537,21 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
std::vector<GCode::LayerToPrint> layers_to_print;
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
// This is the same logic as in support generator.
//FIXME should we use the printing extruders instead?
double gap_over_supports = object.config().support_material_contact_distance;
// FIXME should we test object.config().support_material_synchronize_layers ? Currently the support layers are synchronized with object layers iff soluble supports.
assert(gap_over_supports != 0. || object.config().support_material_synchronize_layers);
if (gap_over_supports != 0.) {
gap_over_supports = std::max(0., gap_over_supports);
// Not a soluble support,
double support_layer_height_min = 1000000.;
for (auto lh : object.print()->config().min_layer_height.values)
support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh));
gap_over_supports += support_layer_height_min;
}
// Pair the object layers with the support layers by z.
size_t idx_object_layer = 0;
size_t idx_support_layer = 0;
@ -559,18 +574,19 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// In case there are extrusions on this layer, check there is a layer to lay it on.
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions())) {
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer)
? object.config().support_material_contact_distance
? gap_over_supports
: 0.;
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
+ layer_to_print.layer()->height
+ std::max(0., support_contact_z);
+ support_contact_z;
// Negative support_contact_z is not taken into account, it can result in false positives in cases
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
if (layer_to_print.print_z() > maximal_print_z + EPSILON)
if (layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
_(L("Object name: ")) + object.model_object()->name + "\n" + _(L("Print z: ")) +
std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "
@ -1853,8 +1869,9 @@ void GCode::process_layer(
if (! m_brim_done) {
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp = true;
for (const ExtrusionEntity *ee : print.brim().entities)
gcode += this->extrude_loop(*dynamic_cast<const ExtrusionLoop*>(ee), "brim", m_config.support_material_speed.value);
for (const ExtrusionEntity *ee : print.brim().entities) {
gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value);
}
m_brim_done = true;
m_avoid_crossing_perimeters.use_external_mp = false;
// Allow a straight travel move to the first object point.
@ -2488,10 +2505,9 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des
return this->extrude_multi_path(*multipath, description, speed);
else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid);
else {
else
throw std::invalid_argument("Invalid argument supplied to extrude()");
return "";
}
return "";
}
std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed)

View file

@ -1,3 +1,5 @@
#include "clipper/clipper_z.hpp"
#include "Print.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
@ -1639,9 +1641,7 @@ void Print::_make_skirt()
// Initial offset of the brim inner edge from the object (possible with a support & raft).
// The skirt will touch the brim if the brim is extruded.
Flow brim_flow = this->brim_flow();
double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
auto distance = float(scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.));
auto distance = float(scale_(m_config.skirt_distance.value) - spacing/2.);
// Draw outlines from outside to inside.
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
@ -1723,12 +1723,134 @@ void Print::_make_brim()
}
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
}
loops = union_pt_chained(loops, false);
// The function above produces ordering well suited for concentric infill (from outside to inside).
// For Brim, the ordering should be reversed (from inside to outside).
std::reverse(loops.begin(), loops.end());
extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
// If there is a possibility that brim intersects skirt, go through loops and split those extrusions
// The result is either the original Polygon or a list of Polylines
if (! m_skirt.empty() && m_config.skirt_distance.value < m_config.brim_width)
{
// Find the bounding polygons of the skirt
const Polygons skirt_inners = offset(dynamic_cast<ExtrusionLoop*>(m_skirt.entities.back())->polygon(),
-float(scale_(this->skirt_flow().spacing()))/2.f,
ClipperLib::jtRound,
float(scale_(0.1)));
const Polygons skirt_outers = offset(dynamic_cast<ExtrusionLoop*>(m_skirt.entities.front())->polygon(),
float(scale_(this->skirt_flow().spacing()))/2.f,
ClipperLib::jtRound,
float(scale_(0.1)));
// First calculate the trimming region.
ClipperLib_Z::Paths trimming;
{
ClipperLib_Z::Paths input_subject;
ClipperLib_Z::Paths input_clip;
for (const Polygon &poly : skirt_outers) {
input_subject.emplace_back();
ClipperLib_Z::Path &out = input_subject.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
for (const Polygon &poly : skirt_inners) {
input_clip.emplace_back();
ClipperLib_Z::Path &out = input_clip.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
// init Clipper
ClipperLib_Z::Clipper clipper;
// add polygons
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
// perform operation
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
}
// Second, trim the extrusion loops with the trimming regions.
ClipperLib_Z::Paths loops_trimmed;
{
// Produce a closed polyline (repeat the first point at the end).
ClipperLib_Z::Paths input_clip;
for (const Polygon &loop : loops) {
input_clip.emplace_back();
ClipperLib_Z::Path& out = input_clip.back();
out.reserve(loop.points.size());
int64_t loop_idx = &loop - &loops.front();
for (const Point& pt : loop.points)
// The Z coordinate carries index of the source loop.
out.emplace_back(pt.x(), pt.y(), loop_idx + 1);
out.emplace_back(out.front());
}
// init Clipper
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
// hat the Z coordinate not set to the contour coordinate.
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z));
});
// add polygons
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
// perform operation
ClipperLib_Z::PolyTree loops_trimmed_tree;
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed);
}
// Third, produce the extrusions, sorted by the source loop indices.
{
std::vector<std::pair<const ClipperLib_Z::Path*, size_t>> loops_trimmed_order;
loops_trimmed_order.reserve(loops_trimmed.size());
for (const ClipperLib_Z::Path &path : loops_trimmed) {
size_t input_idx = 0;
for (const ClipperLib_Z::IntPoint &pt : path)
if (pt.Z > 0) {
input_idx = (size_t)pt.Z;
break;
}
assert(input_idx != 0);
loops_trimmed_order.emplace_back(&path, input_idx);
}
std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(),
[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
return l.second < r.second;
});
Vec3f last_pt(0.f, 0.f, 0.f);
for (size_t i = 0; i < loops_trimmed_order.size();) {
// Find all pieces that the initial loop was split into.
size_t j = i + 1;
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].first == loops_trimmed_order[j].first; ++ j) ;
const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) {
auto *loop = new ExtrusionLoop();
m_brim.entities.emplace_back(loop);
loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
i = j;
} else {
//FIXME this is not optimal as the G-code generator will follow the sequence of paths verbatim without respect to minimum travel distance.
for (; i < j; ++ i) {
m_brim.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())));
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
Points &points = static_cast<ExtrusionPath*>(m_brim.entities.back())->polyline.points;
points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
}
}
}
}
} else {
extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
}
}
// Wipe tower support.

View file

@ -105,6 +105,8 @@
#include <cereal/access.hpp>
#include <cereal/types/base_class.hpp>
#include <clipper/clipper_z.hpp>
#include <clipper/clipper.hpp>
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Config.hpp"