Merge branch 'master' into tm_colldetection_upgr

This commit is contained in:
tamasmeszaros 2019-03-07 12:02:28 +01:00
commit a7c07960ee
116 changed files with 5089 additions and 16124 deletions

View file

@ -42,14 +42,20 @@ public:
bool bridge;
Flow(float _w, float _h, float _nd, bool _bridge = false) :
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {};
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}
float spacing() const;
float spacing(const Flow &other) const;
double mm3_per_mm() const;
coord_t scaled_width() const { return coord_t(scale_(this->width)); };
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); };
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); };
coord_t scaled_width() const { return coord_t(scale_(this->width)); }
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); }
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }
// Elephant foot compensation spacing to be used to detect narrow parts, where the elephant foot compensation cannot be applied.
// To be used on frExternalPerimeter only.
// Enable some perimeter squish (see INSET_OVERLAP_TOLERANCE).
// Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation.
coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); }
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
// Create a flow from the spacing of extrusion lines.

View file

@ -6,6 +6,8 @@
#include "3mf.hpp"
#include <limits>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -1749,6 +1751,11 @@ namespace Slic3r {
bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::stringstream stream;
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
// The default value of std::stream precision is 6 digits only!
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
@ -1864,7 +1871,7 @@ namespace Slic3r {
for (int i = 0; i < stl.stats.shared_vertices; ++i)
{
stream << " <" << VERTEX_TAG << " ";
Vec3d v = matrix * stl.v_shared[i].cast<double>();
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
stream << "x=\"" << v(0) << "\" ";
stream << "y=\"" << v(1) << "\" ";
stream << "z=\"" << v(2) << "\" />\n";

View file

@ -1,3 +1,4 @@
#include <limits>
#include <string.h>
#include <map>
#include <string>
@ -856,6 +857,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
return false;
std::stringstream stream;
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
// The default value of std::stream precision is 6 digits only!
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<amf unit=\"millimeter\">\n";
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
@ -927,7 +933,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) {
stream << " <vertex>\n";
stream << " <coordinates>\n";
Vec3d v = matrix * stl.v_shared[i].cast<double>();
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
stream << " <x>" << v(0) << "</x>\n";
stream << " <y>" << v(1) << "</y>\n";
stream << " <z>" << v(2) << "</z>\n";

View file

@ -1,3 +1,4 @@
#include "libslic3r.h"
#include "GCode.hpp"
#include "ExtrusionEntity.hpp"
#include "EdgeGrid.hpp"
@ -574,6 +575,16 @@ void GCode::_do_export(Print &print, FILE *file)
// resets analyzer
m_analyzer.reset();
// send extruder offset data to analyzer
GCodeAnalyzer::ExtruderOffsetsMap extruder_offsets;
for (unsigned int extruder_id : print.extruders())
{
Vec2d offset = print.config().extruder_offset.get_at(extruder_id);
if (!offset.isApprox(Vec2d::Zero()))
extruder_offsets[extruder_id] = offset;
}
m_analyzer.set_extruder_offsets(extruder_offsets);
// resets analyzer's tracking data
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
m_last_width = GCodeAnalyzer::Default_Width;
@ -843,7 +854,7 @@ void GCode::_do_export(Print &print, FILE *file)
for (unsigned int extruder_id : print.extruders()) {
const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt);
s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
s.translate(Point::new_scale(-extruder_offset(0), -extruder_offset(1)));
skirts.emplace_back(std::move(s));
}
m_ooze_prevention.enable = true;
@ -2488,6 +2499,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// adds analyzer tags and updates analyzer's tracking data
if (m_enable_analyzer)
{
// PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width
// so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines
bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower);
if (path.role() != m_last_analyzer_extrusion_role)
{
m_last_analyzer_extrusion_role = path.role();
@ -2505,7 +2520,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += buf;
}
if (m_last_width != path.width)
if (last_was_wipe_tower || (m_last_width != path.width))
{
m_last_width = path.width;
@ -2514,7 +2529,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += buf;
}
if (m_last_height != path.height)
if (last_was_wipe_tower || (m_last_height != path.height))
{
m_last_height = path.height;

View file

@ -101,6 +101,11 @@ GCodeAnalyzer::GCodeAnalyzer()
reset();
}
void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap& extruder_offsets)
{
m_extruder_offsets = extruder_offsets;
}
void GCodeAnalyzer::reset()
{
_set_units(Millimeters);
@ -118,6 +123,7 @@ void GCodeAnalyzer::reset()
_reset_axes_position();
m_moves_map.clear();
m_extruder_offsets.clear();
}
const std::string& GCodeAnalyzer::process_gcode(const std::string& gcode)
@ -654,7 +660,15 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type)
it = m_moves_map.insert(TypeToMovesMap::value_type(type, GCodeMovesList())).first;
// store move
it->second.emplace_back(type, _get_extrusion_role(), _get_extruder_id(), _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), _get_start_position(), _get_end_position(), _get_delta_extrusion(), _get_cp_color_id());
Vec3d extruder_offset = Vec3d::Zero();
unsigned int extruder_id = _get_extruder_id();
ExtruderOffsetsMap::iterator extr_it = m_extruder_offsets.find(extruder_id);
if (extr_it != m_extruder_offsets.end())
extruder_offset = Vec3d(extr_it->second(0), extr_it->second(1), 0.0);
Vec3d start_position = _get_start_position() + extruder_offset;
Vec3d end_position = _get_end_position() + extruder_offset;
it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id());
}
bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const

View file

@ -86,6 +86,7 @@ public:
typedef std::vector<GCodeMove> GCodeMovesList;
typedef std::map<GCodeMove::EType, GCodeMovesList> TypeToMovesMap;
typedef std::map<unsigned int, Vec2d> ExtruderOffsetsMap;
private:
struct State
@ -104,6 +105,7 @@ private:
State m_state;
GCodeReader m_parser;
TypeToMovesMap m_moves_map;
ExtruderOffsetsMap m_extruder_offsets;
// The output of process_layer()
std::string m_process_output;
@ -111,6 +113,8 @@ private:
public:
GCodeAnalyzer();
void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets);
// Reinitialize the analyzer
void reset();

View file

@ -242,6 +242,7 @@ public:
void set_scaling_factor(const Vec3d& scaling_factor);
void set_scaling_factor(Axis axis, double scaling_factor);
bool is_scaling_uniform() const { return std::abs(m_scaling_factor.x() - m_scaling_factor.y()) < 1e-8 && std::abs(m_scaling_factor.x() - m_scaling_factor.z()) < 1e-8; }
const Vec3d& get_mirror() const { return m_mirror; }
double get_mirror(Axis axis) const { return m_mirror(axis); }

View file

@ -65,6 +65,7 @@ void Layer::make_slices()
this->slices.expolygons.push_back(std::move(slices[i]));
}
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
void Layer::merge_slices()
{
if (m_regions.size() == 1) {
@ -78,6 +79,24 @@ void Layer::merge_slices()
}
}
ExPolygons Layer::merged(float offset_scaled) const
{
assert(offset_scaled >= 0.f);
// If no offset is set, apply EPSILON offset before union, and revert it afterwards.
float offset_scaled2 = 0;
if (offset_scaled == 0.f) {
offset_scaled = float( EPSILON);
offset_scaled2 = float(- EPSILON);
}
Polygons polygons;
for (LayerRegion *layerm : m_regions)
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
ExPolygons out = union_ex(polygons);
if (offset_scaled2 != 0.f)
out = offset_ex(out, offset_scaled2);
return out;
}
// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters.
// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region.
// The resulting fill surface is split back among the originating regions.
@ -86,13 +105,14 @@ void Layer::make_perimeters()
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id();
// keep track of regions whose perimeters we have already generated
std::set<size_t> done;
std::vector<unsigned char> done(m_regions.size(), false);
for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) {
size_t region_id = layerm - m_regions.begin();
if (done.find(region_id) != done.end()) continue;
if (done[region_id])
continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
done.insert(region_id);
done[region_id] = true;
const PrintRegionConfig &config = (*layerm)->region()->config();
// find compatible regions
@ -112,7 +132,7 @@ void Layer::make_perimeters()
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);
done.insert(it - m_regions.begin());
done[it - m_regions.begin()] = true;
}
}
@ -124,15 +144,13 @@ void Layer::make_perimeters()
SurfaceCollection new_slices;
{
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short,Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
for (Surfaces::iterator s = (*l)->slices.surfaces.begin(); s != (*l)->slices.surfaces.end(); ++s) {
slices[s->extra_perimeters].push_back(*s);
}
}
std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegion *layerm : layerms)
for (Surface &surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface);
// merge the surfaces assigned to each group
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
new_slices.append(union_ex(it->second, true), it->second.front());
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front());
}
// make perimeters

View file

@ -63,7 +63,12 @@ public:
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const;
double infill_area_threshold() const;
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
void trim_surfaces(const Polygons &trimming_polygons);
// Single elephant foot compensation step, used by the elephant foor compensation at the 1st layer.
// Trim surfaces by trimming polygons (shrunk by an elephant foot compensation step), but don't shrink narrow parts so much that no perimeter would fit.
void elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons);
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
@ -117,7 +122,10 @@ public:
// Test whether whether there are any slices assigned to this layer.
bool empty() const;
void make_slices();
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
void merge_slices();
// Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices.
ExPolygons merged(float offset) const;
template <class T> bool any_internal_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true;
return false;

View file

@ -74,7 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
// Cummulative sum of polygons over all the regions.
g.lower_slices = &this->layer()->lower_layer->slices;
g.layer_id = this->layer()->id();
g.layer_id = (int)this->layer()->id();
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object());
g.solid_infill_flow = this->flow(frSolidInfill);
@ -385,6 +385,28 @@ double LayerRegion::infill_area_threshold() const
return ss*ss;
}
void LayerRegion::trim_surfaces(const Polygons &trimming_polygons)
{
#ifndef NDEBUG
for (const Surface &surface : this->slices.surfaces)
assert(surface.surface_type == stInternal);
#endif /* NDEBUG */
this->slices.set(intersection_ex(to_polygons(std::move(this->slices.surfaces)), trimming_polygons, false), stInternal);
}
void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons)
{
#ifndef NDEBUG
for (const Surface &surface : this->slices.surfaces)
assert(surface.surface_type == stInternal);
#endif /* NDEBUG */
ExPolygons slices_expolygons = to_expolygons(std::move(this->slices.surfaces));
Polygons slices_polygons = to_polygons(slices_expolygons);
Polygons tmp = intersection(slices_polygons, trimming_polygons, false);
append(tmp, diff(slices_polygons, offset(offset_ex(slices_expolygons, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
this->slices.set(std::move(union_ex(tmp)), stInternal);
}
void LayerRegion::export_region_slices_to_svg(const char *path) const
{
BoundingBox bbox;

View file

@ -592,6 +592,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->input_file = rhs.input_file;
this->config = rhs.config;
this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status;
this->layer_height_ranges = rhs.layer_height_ranges;
this->layer_height_profile = rhs.layer_height_profile;
this->origin_translation = rhs.origin_translation;
@ -625,6 +626,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->input_file = std::move(rhs.input_file);
this->config = std::move(rhs.config);
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation);
@ -1130,6 +1132,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper) {
upper->set_model(nullptr);
upper->sla_support_points.clear();
upper->sla_points_status = sla::PointsStatus::None;
upper->clear_volumes();
upper->input_file = "";
}
@ -1137,6 +1140,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_lower) {
lower->set_model(nullptr);
lower->sla_support_points.clear();
lower->sla_points_status = sla::PointsStatus::None;
lower->clear_volumes();
lower->input_file = "";
}

View file

@ -180,6 +180,9 @@ public:
// saved in mesh coordinates to allow using them for several instances.
// The format is (x, y, z, point_size, supports_island)
std::vector<sla::SupportPoint> sla_support_points;
// To keep track of where the points came from (used for synchronization between
// the SLA gizmo and the backend).
sla::PointsStatus sla_points_status = sla::PointsStatus::None;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation

View file

@ -523,6 +523,9 @@ exit_for_rearrange_regions:
invalidated = true;
}
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
return invalidated;
}
@ -1098,6 +1101,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
}
}
// Update SlicingParameters for each object where the SlicingParameters is not valid.
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
// (posSlicing and posSupportMaterial was invalidated).
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
@ -1202,13 +1211,13 @@ std::string Print::validate() const
break;
}
}
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters();
size_t tallest_object_idx = 0;
if (has_custom_layering)
PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
for (size_t i = 1; i < m_objects.size(); ++ i) {
const PrintObject *object = m_objects[i];
const SlicingParameters slicing_params = object->slicing_parameters();
const PrintObject *object = m_objects[i];
const SlicingParameters &slicing_params = object->slicing_parameters();
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");

View file

@ -93,7 +93,7 @@ public:
const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; }
const Points& copies() const { return m_copies; }
// since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
@ -131,7 +131,7 @@ public:
// by the interactive layer height editor and by the printing process itself.
// The slicing parameters are dependent on various configuration values
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
@ -161,6 +161,8 @@ protected:
bool invalidate_all_steps();
// Invalidate steps based on a set of parameters changed.
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
// If ! m_slicing_params.valid, recalculate.
void update_slicing_parameters();
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
@ -195,11 +197,13 @@ private:
// for external callers)
Point m_copies_shift;
SlicingParameters m_slicing_params;
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> _slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
};
struct WipeTowerData

View file

@ -59,6 +59,41 @@ void PrintConfigDef::init_common_params()
def->cli = "max-print-height=f";
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(200.0);
def = this->add("slice_closing_radius", coFloat);
def->label = L("Slice gap closing radius");
def->category = L("Advanced");
def->tooltip = L("Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. "
"The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low.");
def->sidetext = L("mm");
def->cli = "slice-closing-radius=f";
def->min = 0;
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(0.049);
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the printer host instance.");
def->cli = "print-host=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("printhost_apikey", coString);
def->label = L("API Key / Password");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->cli = "printhost-apikey=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA File";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
def->cli = "printhost-cafile=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
}
void PrintConfigDef::init_fff_params()
@ -1320,30 +1355,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->default_value = new ConfigOptionEnum<PrintHostType>(htOctoPrint);
def = this->add("printhost_apikey", coString);
def->label = L("API Key / Password");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->cli = "printhost-apikey=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA File";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
def->cli = "printhost-cafile=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the printer host instance.");
def->cli = "print-host=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("only_retract_when_crossing_perimeters", coBool);
def->label = L("Only retract when crossing perimeters");
def->tooltip = L("Disables retraction when the travel path does not exceed the upper layer's perimeters "

View file

@ -38,7 +38,7 @@ enum GCodeFlavor {
};
enum PrintHostType {
htOctoPrint, htDuet, htSL1,
htOctoPrint, htDuet
};
enum InfillPattern {
@ -384,6 +384,7 @@ public:
ConfigOptionEnum<SeamPosition> seam_position;
// ConfigOptionFloat seam_preferred_direction;
// ConfigOptionFloat seam_preferred_direction_jitter;
ConfigOptionFloat slice_closing_radius;
ConfigOptionBool support_material;
// Automatic supports (generated based on support_material_threshold).
ConfigOptionBool support_material_auto;
@ -425,6 +426,7 @@ protected:
OPT_PTR(layer_height);
OPT_PTR(raft_layers);
OPT_PTR(seam_position);
OPT_PTR(slice_closing_radius);
// OPT_PTR(seam_preferred_direction);
// OPT_PTR(seam_preferred_direction_jitter);
OPT_PTR(support_material);
@ -963,6 +965,8 @@ public:
//Number of the layers needed for the exposure time fade [3;20]
ConfigOptionInt faded_layers /*= 10*/;
ConfigOptionFloat slice_closing_radius;
// Enabling or disabling support creation
ConfigOptionBool supports_enable;
@ -1036,6 +1040,7 @@ protected:
{
OPT_PTR(layer_height);
OPT_PTR(faded_layers);
OPT_PTR(slice_closing_radius);
OPT_PTR(supports_enable);
OPT_PTR(support_head_front_diameter);
OPT_PTR(support_head_penetration);

View file

@ -157,8 +157,6 @@ template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
"expTimeFirst = " + expt_first_str + "\n"
"numFade = " + cnt_fade_layers + "\n"
"layerHeight = " + layerh_str + "\n"
"expTime = "+expt_str+" + resinType = generic+layerHeight = "
+layerh_str+" + printer = DWARF3\n"
"usedMaterial = " + used_material + "\n"
"numSlow = " + cnt_slow_layers + "\n"
"numFast = " + cnt_fast_layers + "\n";

View file

@ -104,7 +104,7 @@ void PrintObject::slice()
return;
m_print->set_status(10, "Processing triangulated mesh");
std::vector<coordf_t> layer_height_profile;
this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), layer_height_profile);
this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile);
m_print->throw_if_canceled();
this->_slice(layer_height_profile);
m_print->throw_if_canceled();
@ -453,7 +453,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
} else if (
opt_key == "layer_height"
|| opt_key == "first_layer_height"
|| opt_key == "raft_layers") {
|| opt_key == "raft_layers"
|| opt_key == "slice_closing_radius") {
steps.emplace_back(posSlice);
}
else if (
@ -567,8 +568,11 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
} else if (step == posSupportMaterial)
this->m_slicing_params.valid = false;
} else if (step == posSupportMaterial) {
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false;
}
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
// It also decides about what the wipe_into_infill / wipe_into_object features will do,
@ -806,7 +810,7 @@ void PrintObject::process_external_surfaces()
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
m_layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]);
m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]);
}
}
);
@ -1359,11 +1363,11 @@ PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegion
return config;
}
SlicingParameters PrintObject::slicing_parameters() const
void PrintObject::update_slicing_parameters()
{
return SlicingParameters::create_from_config(
this->print()->config(), m_config,
unscale<double>(this->size(2)), this->object_extruders());
if (! m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
}
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object)
@ -1450,23 +1454,21 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
tbb_init = new tbb::task_scheduler_init(1);
#endif
SlicingParameters slicing_params = this->slicing_parameters();
// 1) Initialize layers and their slice heights.
std::vector<float> slice_zs;
{
this->clear_layers();
// Object layers (pairs of bottom/top Z coordinate), without the raft.
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, layer_height_profile);
std::vector<coordf_t> object_layers = generate_object_layers(m_slicing_params, layer_height_profile);
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
int id = int(slicing_params.raft_layers());
int id = int(m_slicing_params.raft_layers());
slice_zs.reserve(object_layers.size());
Layer *prev = nullptr;
for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z);
Layer *layer = this->add_layer(id ++, hi - lo, hi + m_slicing_params.object_print_z_min, slice_z);
slice_zs.push_back(float(slice_z));
if (prev != nullptr) {
prev->upper_layer = layer;
@ -1478,46 +1480,151 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
prev = layer;
}
}
// Count model parts and modifier meshes, check whether the model parts are of the same region.
int single_volume_region = -2; // not set yet
size_t num_volumes = 0;
size_t num_modifiers = 0;
std::vector<int> map_volume_to_region(this->model_object()->volumes.size());
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
for (int volume_id : this->region_volumes[region_id]) {
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
map_volume_to_region[volume_id] = region_id;
if (single_volume_region == -2)
// first model volume met
single_volume_region = region_id;
else if (single_volume_region != region_id)
// multiple volumes met and they are not equal
single_volume_region = -1;
++ num_volumes;
} else if (model_volume->is_modifier())
++ num_modifiers;
}
}
assert(num_volumes > 0);
// Slice all non-modifier volumes.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end";
bool clipped = false;
bool upscaled = false;
if (! m_config.clip_multipart_objects.value || single_volume_region >= 0) {
// Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end";
}
} else {
// Expensive path: Slice one volume after the other in the order they are presented at the user interface,
// clip the last volumes with the first.
// First slice the volumes.
struct SlicedVolume {
SlicedVolume(int volume_id, int region_id, std::vector<ExPolygons> &&expolygons_by_layer) :
volume_id(volume_id), region_id(region_id), expolygons_by_layer(std::move(expolygons_by_layer)) {}
int volume_id;
int region_id;
std::vector<ExPolygons> expolygons_by_layer;
};
std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
for (int volume_id : this->region_volumes[region_id]) {
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
// slicing in parallel
sliced_volumes.emplace_back(volume_id, map_volume_to_region[volume_id], this->_slice_volume(slice_zs, *model_volume));
}
}
// Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, slice_zs.size()),
[this, &sliced_volumes, num_modifiers](const tbb::blocked_range<size_t>& range) {
float delta = float(scale_(m_config.xy_size_compensation.value));
// Only upscale together with clipping if there are no modifiers, as the modifiers shall be applied before upscaling
// (upscaling may grow the object outside of the modifier mesh).
bool upscale = delta > 0 && num_modifiers == 0;
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled();
// Trim volumes in a single layer, one by the other, possibly apply upscaling.
{
Polygons processed;
for (SlicedVolume &sliced_volume : sliced_volumes) {
ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]);
if (upscale)
slices = offset_ex(std::move(slices), delta);
if (! processed.empty())
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
sliced_volume.expolygons_by_layer[layer_id] = std::move(slices);
}
}
// Collect and union volumes of a single region.
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
ExPolygons expolygons;
size_t num_volumes = 0;
for (SlicedVolume &sliced_volume : sliced_volumes)
if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer[layer_id].empty()) {
++ num_volumes;
append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id]));
}
if (num_volumes > 1)
// Merge the islands using a positive / negative offset.
expolygons = offset_ex(offset_ex(expolygons, float(scale_(EPSILON))), -float(scale_(EPSILON)));
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons), stInternal);
}
}
});
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - end";
clipped = true;
upscaled = m_config.xy_size_compensation.value > 0 && num_modifiers == 0;
}
// Slice all modifier volumes.
if (this->region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
m_print->throw_if_canceled();
if (expolygons_by_layer.empty())
continue;
// loop through the other regions and 'steal' the slices belonging to this one
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end";
}
@ -1540,38 +1647,94 @@ end:
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
[this, upscaled, clipped](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled();
Layer *layer = m_layers[layer_id];
// Apply size compensation and perform clipping of multi-part objects.
float delta = float(scale_(m_config.xy_size_compensation.value));
float elephant_foot_compensation = 0.f;
if (layer_id == 0)
delta -= float(scale_(m_config.elefant_foot_compensation.value));
bool scale = delta != 0.f;
bool clip = m_config.clip_multipart_objects.value || delta > 0.f;
elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value));
if (layer->m_regions.size() == 1) {
if (scale) {
// Optimized version for a single region layer.
if (layer_id == 0) {
if (delta > elephant_foot_compensation) {
delta -= elephant_foot_compensation;
elephant_foot_compensation = 0.f;
} else if (delta > 0)
elephant_foot_compensation -= delta;
}
if (delta != 0.f || elephant_foot_compensation > 0.f) {
// Single region, growing or shrinking.
LayerRegion *layerm = layer->m_regions.front();
layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal);
// Apply the XY compensation.
ExPolygons expolygons = (delta == 0.f) ?
to_expolygons(std::move(layerm->slices.surfaces)) :
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
// Apply the elephant foot compensation.
if (elephant_foot_compensation > 0) {
float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing());
float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
size_t nsteps = size_t(steps);
float step = elephant_foot_compensation / steps;
for (size_t i = 0; i < nsteps; ++ i) {
Polygons tmp = offset(expolygons, - step);
append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing - step), elephant_foot_spacing + step)));
expolygons = union_ex(tmp);
}
}
layerm->slices.set(std::move(expolygons), stInternal);
}
} else if (scale || clip) {
// Multiple regions, growing, shrinking or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions.
Polygons processed;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
if (scale)
slices = offset_ex(slices, delta);
if (region_id > 0 && clip)
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (clip && region_id + 1 < layer->m_regions.size())
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
} else {
bool upscale = ! upscaled && delta > 0.f;
bool clip = ! clipped && m_config.clip_multipart_objects.value;
if (upscale || clip) {
// Multiple regions, growing or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions.
Polygons processed;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
if (upscale)
slices = offset_ex(std::move(slices), delta);
if (region_id > 0 && clip)
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (clip && (region_id + 1 < layer->m_regions.size()))
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
}
}
if (delta < 0.f) {
// Apply the negative XY compensation.
Polygons trimming = offset(layer->merged(float(EPSILON)), delta - float(EPSILON));
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->trim_surfaces(trimming);
}
if (elephant_foot_compensation > 0.f) {
// Apply the elephant foot compensation.
std::vector<float> elephant_foot_spacing;
elephant_foot_spacing.reserve(layer->m_regions.size());
float external_perimeter_nozzle = 0.f;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
elephant_foot_spacing.emplace_back(float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing()));
external_perimeter_nozzle += float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
}
external_perimeter_nozzle /= (float)layer->m_regions.size();
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
size_t nsteps = size_t(steps);
float step = elephant_foot_compensation / steps;
for (size_t i = 0; i < nsteps; ++ i) {
Polygons trimming_polygons = offset(layer->merged(float(EPSILON)), - step - float(EPSILON));
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] + step, trimming_polygons);
}
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
@ -1643,13 +1806,35 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mslicer.init(&mesh, callback);
mslicer.slice(z, &layers, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
}
}
return layers;
}
std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh);
mesh.transform(volume.get_matrix());
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo);
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
}
return layers;
}
std::string PrintObject::_fix_slicing_errors()
{
// Collect layers with slicing errors.
@ -2245,7 +2430,7 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
PrintObjectSupportMaterial support_material(this, m_slicing_params);
support_material.generate(*this);
}

View file

@ -144,6 +144,8 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]);
const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports
const float slope_offset = float(scale_(layer_height / std::tan(slope_angle)));
//FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands.
for (SLAAutoSupports::Structure &top : layer_above.islands) {
for (SLAAutoSupports::Structure &bottom : layer_below.islands) {
@ -173,6 +175,7 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
overhangs_sorted.emplace_back(std::move(*p.first));
top.overhangs = std::move(overhangs_sorted);
top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR);
top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset));
top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset));
}
}
@ -241,9 +244,9 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
// What we now have in polygons needs support, regardless of what the forces are, so we can add them.
//FIXME is it an island point or not? Vojtech thinks it is.
uniformly_cover(s.dangling_areas, s, point_grid);
} else if (! s.overhangs.empty()) {
} else if (! s.overhangs_slopes.empty()) {
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
uniformly_cover(s.overhangs, s, point_grid);
uniformly_cover(s.overhangs_slopes, s, point_grid);
}
}

View file

@ -65,8 +65,12 @@ public:
std::vector<Link> islands_above;
std::vector<Link> islands_below;
#endif
// Overhangs, that are dangling considerably.
ExPolygons dangling_areas;
// Complete overhands.
ExPolygons overhangs;
// Overhangs, where the surface must slope.
ExPolygons overhangs_slopes;
float overhangs_area;
bool overlaps(const Structure &rhs) const {

View file

@ -561,7 +561,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
heights.emplace_back(hi);
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
slicer.slice(heights, &out, thrfn);
slicer.slice(heights, 0.f, &out, thrfn);
size_t count = 0; for(auto& o : out) count += o.size();

View file

@ -16,6 +16,14 @@ class TriangleMesh;
namespace sla {
// An enum to keep track of where the current points on the ModelObject came from.
enum class PointsStatus {
None, // No points were generated so far.
Generating, // The autogeneration algorithm triggered, but not yet finished.
AutoGenerated, // Points were autogenerated (i.e. copied from the backend).
UserModified // User has done some edits.
};
struct SupportPoint {
Vec3f pos;
float head_front_radius;

View file

@ -2129,7 +2129,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
fullmesh.merge(get_pad());
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
slicer.slice(heights, &ret, get().ctl().cancelfn);
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
return ret;
}

View file

@ -337,11 +337,34 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
}
}
}
if (model_object.sla_support_points != model_object_new.sla_support_points) {
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
model_object.sla_support_points = model_object_new.sla_support_points;
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
}
if (model_object.sla_points_status != model_object_new.sla_points_status) {
// Change of this status should invalidate support points. The points themselves are not enough, there are none
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
// invalidate - that would keep stopping the background processing without a reason.
if (model_object.sla_points_status != sla::PointsStatus::Generating)
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
}*/
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
model_object.sla_support_points = model_object_new.sla_support_points;
}
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
@ -613,7 +636,7 @@ void SLAPrint::process()
ilh, float(lh));
auto& layers = po.m_model_slices; layers.clear();
slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
slicer.slice(heights, float(po.config().slice_closing_radius.value), &layers, [this](){ throw_if_canceled(); });
};
// In this step we check the slices, identify island and cover them with
@ -629,10 +652,11 @@ void SLAPrint::process()
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
// If there are no points on the front-end, we will do the
// autoplacement. Otherwise we will just blindly copy the frontend data
// Unless the user modified the points or we already did the calculation, we will do
// the autoplacement. Otherwise we will just blindly copy the frontend data
// into the backend cache.
if(mo.sla_support_points.empty()) {
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
// calculate heights of slices (slices are calculated already)
double lh = po.m_config.layer_height.getFloat();
@ -644,7 +668,9 @@ void SLAPrint::process()
this->throw_if_canceled();
SLAAutoSupports::Config config;
const SLAPrintObjectConfig& cfg = po.config();
config.density_relative = float(cfg.support_points_density_relative / 100.f); // the config value is in percents
// the density config value is in percents:
config.density_relative = float(cfg.support_points_density_relative / 100.f);
config.minimal_distance = float(cfg.support_points_minimal_distance);
// Construction of this object does the calculation.
@ -668,7 +694,7 @@ void SLAPrint::process()
report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
}
else {
// There are some points on the front-end, no calculation will be done.
// There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
po.m_supportdata->support_points = po.transformed_support_points();
}
};
@ -1341,7 +1367,8 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
bool invalidated = false;
for (const t_config_option_key &opt_key : opt_keys) {
if ( opt_key == "layer_height"
|| opt_key == "faded_layers") {
|| opt_key == "faded_layers"
|| opt_key == "slice_closing_radius") {
steps.emplace_back(slaposObjectSlice);
} else if (
opt_key == "supports_enable"

View file

@ -149,6 +149,7 @@ SlicingParameters SlicingParameters::create_from_config(
params.object_print_z_max += print_z;
}
params.valid = true;
return params;
}

View file

@ -42,6 +42,8 @@ struct SlicingParameters
// Height of the object to be printed. This value does not contain the raft height.
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
bool valid;
// Number of raft layers.
size_t base_raft_layers;
// Number of interface layers including the contact layer.
@ -100,6 +102,8 @@ static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
{
assert(sp1.valid);
assert(sp2.valid);
return sp1.base_raft_layers == sp2.base_raft_layers &&
sp1.interface_raft_layers == sp2.interface_raft_layers &&
sp1.base_raft_layer_height == sp2.base_raft_layer_height &&

View file

@ -42,15 +42,6 @@
#define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4)
//====================
// 1.42.0.alpha5 techs
//====================
#define ENABLE_1_42_0_ALPHA5 1
// Toolbar items hidden/shown in dependence of the user mode
#define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5)
//====================
// 1.42.0.alpha7 techs
//====================
@ -59,4 +50,13 @@
// Printbed textures generated from svg files
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
//====================
// 1.42.0.alpha8 techs
//====================
#define ENABLE_1_42_0_ALPHA8 1
// Toolbars and Gizmos use icons imported from svg files
#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG)
#endif // _technologies_h_

View file

@ -852,7 +852,7 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
}
}
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
void TriangleMeshSlicer::slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p, throw_on_cancel);
@ -861,13 +861,13 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
[&layers_p, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
throw_on_cancel();
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
this->make_expolygons(layers_p[layer_id], closing_radius, &(*layers)[layer_id]);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
@ -1600,7 +1600,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
#endif
}
void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const
{
/*
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
@ -1655,7 +1655,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
// 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);
double safety_offset = scale_(closing_radius);
/* The following line is commented out because it can generate wrong polygons,
see for example issue #661 */
@ -1670,17 +1670,17 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
#endif
// append to the supplied collection
/* Fix for issue #661 { */
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
//expolygons_append(*slices, ex_slices);
/* } */
if (safety_offset > 0)
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
else
expolygons_append(*slices, union_ex(loops, false));
}
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const
{
Polygons pp;
this->make_loops(lines, &pp);
this->make_expolygons(pp, slices);
this->make_expolygons(pp, closing_radius, slices);
}
void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const

View file

@ -165,7 +165,7 @@ public:
TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); }
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
enum FacetSliceType {
NoSlice = 0,
Slicing = 1,
@ -184,9 +184,9 @@ private:
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
void make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const;
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const;
};
TriangleMesh make_cube(double x, double y, double z);