mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-23 14:44:19 -06:00
Merge branch 'master' into dev
This commit is contained in:
commit
a9182fb0b3
11 changed files with 292 additions and 51 deletions
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue