Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-05-13 14:10:42 +02:00
commit 6bddacee44
90 changed files with 4276 additions and 4185 deletions

View file

@ -163,6 +163,7 @@ add_library(libslic3r STATIC
AppConfig.hpp
Print.cpp
Print.hpp
PrintApply.cpp
PrintBase.cpp
PrintBase.hpp
PrintConfig.cpp

View file

@ -546,7 +546,7 @@ bool EdgeGrid::Grid::inside(const Point &pt_src)
return false;
coord_t ix = p(0) / m_resolution;
coord_t iy = p(1) / m_resolution;
if (ix >= this->m_cols || iy >= this->m_rows)
if (ix >= m_cols || iy >= m_rows)
return false;
size_t i_closest = (size_t)-1;

View file

@ -122,10 +122,10 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
if (surface.surface_type == stInternalVoid)
has_internal_voids = true;
else {
const PrintRegionConfig &region_config = layerm.region()->config();
const PrintRegionConfig &region_config = layerm.region().config();
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
bool is_bridge = layer.id() > 0 && surface.is_bridge();
params.extruder = layerm.region()->extruder(extrusion_role);
params.extruder = layerm.region().extruder(extrusion_role);
params.pattern = region_config.fill_pattern.value;
params.density = float(region_config.fill_density);
@ -162,7 +162,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
} else {
// Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status,
// so that internall infill will be aligned over all layers of the current region.
params.spacing = layerm.region()->flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing();
params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing();
// Anchor a sparse infill to inner perimeters with the following anchor length:
params.anchor_length = float(region_config.infill_anchor);
if (region_config.infill_anchor.percent)
@ -274,11 +274,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
}
if (internal_solid_fill == nullptr) {
// Produce another solid fill.
params.extruder = layerm.region()->extruder(frSolidInfill);
params.pattern = layerm.region()->config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
params.extruder = layerm.region().extruder(frSolidInfill);
params.pattern = layerm.region().config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
params.density = 100.f;
params.extrusion_role = erInternalInfill;
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
params.angle = float(Geometry::deg2rad(layerm.region().config().fill_angle.value));
// calculate the actual flow we'll be using for this infill
params.flow = layerm.flow(frSolidInfill);
params.spacing = params.flow.spacing();
@ -501,7 +501,7 @@ void Layer::make_ironing()
for (LayerRegion *layerm : m_regions)
if (! layerm->slices.empty()) {
IroningParams ironing_params;
const PrintRegionConfig &config = layerm->region()->config();
const PrintRegionConfig &config = layerm->region().config();
if (config.ironing &&
(config.ironing_type == IroningType::AllSolid ||
(config.top_solid_layers > 0 &&
@ -556,7 +556,7 @@ void Layer::make_ironing()
Polygons infills;
for (size_t k = i; k < j; ++ k) {
const IroningParams &ironing_params = by_extruder[k];
const PrintRegionConfig &region_config = ironing_params.layerm->region()->config();
const PrintRegionConfig &region_config = ironing_params.layerm->region().config();
bool iron_everything = region_config.ironing_type == IroningType::AllSolid;
bool iron_completely = iron_everything;
if (iron_everything) {

View file

@ -291,13 +291,13 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
double extrusion_width;
};
std::vector<RegionFillData> region_fill_data;
region_fill_data.reserve(print_object.print()->regions().size());
region_fill_data.reserve(print_object.num_printing_regions());
bool build_octree = false;
const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
for (const PrintRegion *region : print_object.print()->regions()) {
const PrintRegionConfig &config = region->config();
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegionConfig &config = print_object.printing_region(region_id).config();
bool nonempty = config.fill_density > 0;
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;

View file

@ -611,12 +611,47 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
// free functions called by GCode::do_export()
namespace DoExport {
static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
// static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
// {
// const GCodeProcessor::Result& result = processor.get_result();
// print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
// print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
// get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
// }
static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector<Extruder>& extruders, PrintStatistics& print_statistics)
{
const GCodeProcessor::Result& result = processor.get_result();
print_statistics.estimated_normal_print_time = get_time_dhms(result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].time);
print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
get_time_dhms(result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].time) : "N/A";
get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
// update filament statictics
double total_extruded_volume = 0.0;
double total_used_filament = 0.0;
double total_weight = 0.0;
double total_cost = 0.0;
for (auto volume : result.print_statistics.volumes_per_extruder) {
total_extruded_volume += volume.second;
size_t extruder_id = volume.first;
auto extruder = std::find_if(extruders.begin(), extruders.end(), [extruder_id](const Extruder& extr) { return extr.id() == extruder_id; });
if (extruder == extruders.end())
continue;
double s = PI * sqr(0.5* extruder->filament_diameter());
double weight = volume.second * extruder->filament_density() * 0.001;
total_used_filament += volume.second/s;
total_weight += weight;
total_cost += weight * extruder->filament_cost() * 0.001;
}
print_statistics.total_extruded_volume = total_extruded_volume;
print_statistics.total_used_filament = total_used_filament;
print_statistics.total_weight = total_weight;
print_statistics.total_cost = total_cost;
print_statistics.filament_stats = result.print_statistics.volumes_per_extruder;
}
#if ENABLE_VALIDATE_CUSTOM_GCODE
@ -754,7 +789,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
#if ENABLE_GCODE_WINDOW
if (result != nullptr) {
*result = std::move(m_processor.extract_result());
@ -796,19 +832,19 @@ namespace DoExport {
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
for (auto object : print.objects()) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const PrintRegion* region = print.regions()[region_id];
for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) {
const PrintRegion &region = object->printing_region(region_id);
for (auto layer : object->layers()) {
const LayerRegion* layerm = layer->regions()[region_id];
if (region->config().get_abs_value("perimeter_speed") == 0 ||
region->config().get_abs_value("small_perimeter_speed") == 0 ||
region->config().get_abs_value("external_perimeter_speed") == 0 ||
region->config().get_abs_value("bridge_speed") == 0)
if (region.config().get_abs_value("perimeter_speed") == 0 ||
region.config().get_abs_value("small_perimeter_speed") == 0 ||
region.config().get_abs_value("external_perimeter_speed") == 0 ||
region.config().get_abs_value("bridge_speed") == 0)
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
if (region->config().get_abs_value("infill_speed") == 0 ||
region->config().get_abs_value("solid_infill_speed") == 0 ||
region->config().get_abs_value("top_solid_infill_speed") == 0 ||
region->config().get_abs_value("bridge_speed") == 0)
if (region.config().get_abs_value("infill_speed") == 0 ||
region.config().get_abs_value("solid_infill_speed") == 0 ||
region.config().get_abs_value("top_solid_infill_speed") == 0 ||
region.config().get_abs_value("bridge_speed") == 0)
{
// Minimal volumetric flow should not be calculated over ironing extrusions.
// Use following lambda instead of the built-it method.
@ -957,7 +993,6 @@ namespace DoExport {
dst.first += buf;
++ dst.second;
};
print_statistics.filament_stats.insert(std::pair<size_t, float>{extruder.id(), (float)used_filament});
append(out_filament_used_mm, "%.2lf", used_filament);
append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
if (filament_weight > 0.) {
@ -1112,16 +1147,17 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
const double layer_height = first_object->config().layer_height.value;
assert(! print.config().first_layer_height.percent);
const double first_layer_height = print.config().first_layer_height.value;
for (const PrintRegion* region : print.regions()) {
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width());
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width());
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width());
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width());
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width());
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
const PrintRegion &region = print.get_print_region(region_id);
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
_write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
_write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
if (print.has_support_material())
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
if (print.config().first_layer_extrusion_width.value > 0)
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width());
_write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
_write_format(file, "\n");
}
print.throw_if_canceled();
@ -1935,7 +1971,7 @@ void GCode::process_layer(
bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
if (enable) {
for (const LayerRegion *layer_region : layer.regions())
if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() ||
if (size_t(layer_region->region().config().bottom_solid_layers.value) > layer.id() ||
layer_region->perimeters.items_count() > 1u ||
layer_region->fills.items_count() > 0) {
enable = false;
@ -2109,7 +2145,9 @@ void GCode::process_layer(
const LayerRegion *layerm = layer.regions()[region_id];
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions()[region_id];
// PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not
// identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion.
const PrintRegion &region = print.get_print_region(layerm->region().print_region_id());
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
// It is also necessary to save which extrusions are part of MM wiping and which are not.
@ -2167,8 +2205,8 @@ void GCode::process_layer(
// extrusions->first_point fits inside ith slice
point_inside_surface(island_idx, extrusions->first_point())) {
if (islands[island_idx].by_region.empty())
islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides);
islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides);
break;
}
}
@ -2570,7 +2608,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region)
if (! region.perimeters.empty()) {
m_config.apply(print.regions()[&region - &by_region.front()]->config());
m_config.apply(print.get_print_region(&region - &by_region.front()).config());
for (const ExtrusionEntity *ee : region.perimeters)
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
}
@ -2591,7 +2629,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
if ((ee->role() == erIroning) == ironing)
extrusions.emplace_back(ee);
if (! extrusions.empty()) {
m_config.apply(print.regions()[&region - &by_region.front()]->config());
m_config.apply(print.get_print_region(&region - &by_region.front()).config());
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
for (const ExtrusionEntity *fill : extrusions) {
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);

View file

@ -186,6 +186,72 @@ void GCodeProcessor::TimeMachine::CustomGCodeTime::reset()
times = std::vector<std::pair<CustomGCode::Type, float>>();
}
void GCodeProcessor::UsedFilaments::reset()
{
color_change_cache = 0.0f;
volumes_per_color_change = std::vector<double>();
tool_change_cache = 0.0f;
volumes_per_extruder.clear();
role_cache = 0.0f;
filaments_per_role.clear();
}
void GCodeProcessor::UsedFilaments::increase_caches(double extruded_volume)
{
color_change_cache += extruded_volume;
tool_change_cache += extruded_volume;
role_cache += extruded_volume;
}
void GCodeProcessor::UsedFilaments::process_color_change_cache()
{
if (color_change_cache != 0.0f) {
volumes_per_color_change.push_back(color_change_cache);
color_change_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_extruder_cache(GCodeProcessor* processor)
{
size_t active_extruder_id = processor->m_extruder_id;
if (tool_change_cache != 0.0f) {
if (volumes_per_extruder.find(active_extruder_id) != volumes_per_extruder.end())
volumes_per_extruder[active_extruder_id] += tool_change_cache;
else
volumes_per_extruder[active_extruder_id] = tool_change_cache;
tool_change_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor)
{
if (role_cache != 0.0f) {
std::pair<double, double> filament = { 0.0f, 0.0f };
double s = PI * sqr(0.5 * processor->m_filament_diameters[processor->m_extruder_id]);
filament.first = role_cache/s * 0.001;
filament.second = role_cache * processor->m_filament_densities[processor->m_extruder_id] * 0.001;
ExtrusionRole active_role = processor->m_extrusion_role;
if (filaments_per_role.find(active_role) != filaments_per_role.end()) {
filaments_per_role[active_role].first += filament.first;
filaments_per_role[active_role].second += filament.second;
}
else
filaments_per_role[active_role] = filament;
role_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
{
process_color_change_cache();
process_extruder_cache(processor);
process_role_cache(processor);
}
void GCodeProcessor::TimeMachine::reset()
{
enabled = false;
@ -348,10 +414,10 @@ void GCodeProcessor::TimeProcessor::reset()
machine_limits = MachineEnvelopeConfig();
filament_load_times = std::vector<float>();
filament_unload_times = std::vector<float>();
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
machines[i].reset();
}
machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].enabled = true;
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
}
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
@ -416,19 +482,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
size_t g1_lines_counter = 0;
// keeps track of last exported pair <percent, remaining time>
#if ENABLE_EXTENDED_M73_LINES
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_main;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
}
// keeps track of last exported remaining time to next printer stop
std::array<int, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_stop;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
std::array<int, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported_stop[i] = time_in_minutes(machines[i].time);
}
#else
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported[i] = { 0, time_in_minutes(machines[i].time) };
}
#endif // ENABLE_EXTENDED_M73_LINES
@ -451,7 +517,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
line = line.substr(1);
if (export_remaining_time_enabled &&
(line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i];
if (machine.enabled) {
#if ENABLE_EXTENDED_M73_LINES
@ -486,7 +552,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
#else
if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i];
if (machine.enabled) {
ret += format_line_M73(machine.line_m73_mask.c_str(),
@ -497,13 +563,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
}
else if (line == Estimated_Printing_Time_Placeholder_Tag) {
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i];
PrintEstimatedTimeStatistics::ETimeMode mode = static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i);
if (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal || machine.enabled) {
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128];
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
(mode == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.time).c_str());
ret += buf;
}
@ -545,7 +611,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
unsigned int exported_lines_count = 0;
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (export_remaining_time_enabled) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i];
if (machine.enabled) {
// export pair <percent, remaining time>
@ -789,13 +855,13 @@ GCodeProcessor::GCodeProcessor()
{
reset();
#if ENABLE_EXTENDED_M73_LINES
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n";
#else
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n";
#endif // ENABLE_EXTENDED_M73_LINES
}
@ -826,6 +892,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
}
m_filament_densities.resize(config.filament_density.values.size());
for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
m_filament_densities[i] = static_cast<float>(config.filament_density.values[i]);
}
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
if (m_flavor == gcfMarlinLegacy) {
@ -846,7 +917,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_time_processor.filament_unload_times[i] = static_cast<float>(config.filament_unload_time.values[i]);
}
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
@ -896,6 +967,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
}
}
const ConfigOptionFloats* filament_densities = config.option<ConfigOptionFloats>("filament_density");
if (filament_densities != nullptr) {
for (double dens : filament_densities->values) {
m_filament_densities.push_back(static_cast<float>(dens));
}
}
m_result.extruders_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
const ConfigOptionPoints* extruder_offset = config.option<ConfigOptionPoints>("extruder_offset");
@ -1026,7 +1104,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
}
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
@ -1051,7 +1129,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
{
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled = enabled;
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled = enabled;
}
void GCodeProcessor::reset()
@ -1096,6 +1174,7 @@ void GCodeProcessor::reset()
}
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
m_filament_densities = std::vector<float>(Min_Extruder_Count, 1.245f);
m_extruded_last_z = 0.0f;
#if ENABLE_START_GCODE_VISUALIZATION
m_first_layer_height = 0.0f;
@ -1109,6 +1188,7 @@ void GCodeProcessor::reset()
m_producers_enabled = false;
m_time_processor.reset();
m_used_filaments.reset();
m_result.reset();
m_result.id = ++s_result_id;
@ -1186,7 +1266,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
}
// process the time blocks
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i];
TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time;
machine.calculate_time();
@ -1194,6 +1274,8 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache });
}
m_used_filaments.process_caches(this);
update_estimated_times_stats();
// post-process to add M73 lines into the gcode
@ -1216,20 +1298,20 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
#endif // ENABLE_GCODE_VIEWER_STATISTICS
}
float GCodeProcessor::get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const
{
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
}
std::string GCodeProcessor::get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const
std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const
{
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
}
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const
{
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> ret;
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) {
if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
const TimeMachine& machine = m_time_processor.machines[static_cast<size_t>(mode)];
float total_time = 0.0f;
for (const auto& [type, time] : machine.gcode_time.times) {
@ -1241,10 +1323,10 @@ std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcesso
return ret;
}
std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const
{
std::vector<std::pair<EMoveType, float>> ret;
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) {
if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].moves_time.size(); ++i) {
float time = m_time_processor.machines[static_cast<size_t>(mode)].moves_time[i];
if (time > 0.0f)
@ -1254,10 +1336,10 @@ std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEst
return ret;
}
std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const
{
std::vector<std::pair<ExtrusionRole, float>> ret;
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) {
if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].roles_time.size(); ++i) {
float time = m_time_processor.machines[static_cast<size_t>(mode)].roles_time[i];
if (time > 0.0f)
@ -1267,9 +1349,9 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
return ret;
}
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
{
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ?
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
m_time_processor.machines[static_cast<size_t>(mode)].layers_time :
std::vector<float>();
}
@ -1461,6 +1543,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_VALIDATE_CUSTOM_GCODE
// extrusion role tag
if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
m_used_filaments.process_role_cache(this);
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()));
#if ENABLE_SEAMS_VISUALIZATION
if (m_extrusion_role == erExternalPerimeter)
@ -1546,7 +1629,8 @@ void GCodeProcessor::process_tags(const std::string_view comment)
extruder_id = static_cast<unsigned char>(eid);
}
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
if (extruder_id < m_extruder_colors.size())
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
++m_cp_color.counter;
if (m_cp_color.counter == UCHAR_MAX)
m_cp_color.counter = 0;
@ -1557,6 +1641,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
}
process_custom_gcode_time(CustomGCode::ColorChange);
process_filaments(CustomGCode::ColorChange);
return;
}
@ -2194,6 +2279,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
// save extruded volume to the cache
m_used_filaments.increase_caches(volume_extruded_filament);
// volume extruded filament / tool displacement = area toolpath cross section
m_mm3_per_mm = area_toolpath_cross_section;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
@ -2254,7 +2342,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
assert(distance != 0.0f);
float inv_distance = 1.0f / distance;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i];
if (!machine.enabled)
continue;
@ -2264,8 +2352,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
std::vector<TimeBlock>& blocks = machine.blocks;
curr.feedrate = (delta_pos[E] == 0.0f) ?
minimum_travel_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), m_feedrate) :
minimum_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), m_feedrate);
minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
minimum_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate);
TimeBlock block;
block.move_type = type;
@ -2283,7 +2371,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (axis_max_feedrate != 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
}
@ -2300,13 +2388,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// calculates block acceleration
float acceleration =
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
(is_extrusion_only_move(delta_pos) ?
get_retract_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
get_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)));
get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration;
}
@ -2317,7 +2405,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
curr.safe_feedrate = block.feedrate_profile.cruise;
for (unsigned char a = X; a <= E; ++a) {
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (curr.abs_axis_feedrate[a] > axis_max_jerk)
curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk);
}
@ -2365,7 +2453,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// axis reversal
std::max(-v_exit, v_entry));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
@ -2650,8 +2738,8 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
@ -2678,8 +2766,8 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
// http://smoothieware.org/supported-g-codes
float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
@ -2699,27 +2787,27 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
{
float value;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_value('S', value)) {
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware
// It is also generated by PrusaSlicer to control acceleration per extrusion type
// (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used.
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
}
else {
// New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value))
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('R', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
if (line.has_value('T', value))
// Interpret the T value as the travel acceleration in the new Marlin format.
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
}
}
}
@ -2727,8 +2815,8 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
{
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) {
float max_jerk = line.x();
@ -2761,7 +2849,7 @@ void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line)
float value_t;
if (line.has_value('S', value_s) && !line.has_value('T', value_t)) {
value_s *= 0.01f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
m_time_processor.machines[i].extrude_factor_override_percentage = value_s;
}
}
@ -2812,7 +2900,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line)
{
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC);
@ -2863,6 +2951,7 @@ void GCodeProcessor::process_T(const std::string_view command)
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode.";
else {
unsigned char old_extruder_id = m_extruder_id;
process_filaments(CustomGCode::ToolChange);
m_extruder_id = id;
m_cp_color.current = m_extruder_colors[id];
// Specific to the MK3 MMU2:
@ -2920,7 +3009,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
#if ENABLE_EXTENDED_M73_LINES
// stores stop time placeholders for later use
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i];
if (!machine.enabled)
continue;
@ -2931,7 +3020,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
#endif // ENABLE_EXTENDED_M73_LINES
}
float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const
float GCodeProcessor::minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const
{
if (m_time_processor.machine_limits.machine_min_extruding_rate.empty())
return feedrate;
@ -2939,7 +3028,7 @@ float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode m
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast<size_t>(mode)));
}
float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const
float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const
{
if (m_time_processor.machine_limits.machine_min_travel_rate.empty())
return feedrate;
@ -2947,7 +3036,7 @@ float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETim
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast<size_t>(mode)));
}
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
switch (axis)
{
@ -2959,7 +3048,7 @@ float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeM
}
}
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
switch (axis)
{
@ -2971,7 +3060,7 @@ float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ET
}
}
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
switch (axis)
{
@ -2983,18 +3072,18 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode
}
}
float GCodeProcessor::get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
}
float GCodeProcessor::get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
size_t id = static_cast<size_t>(mode);
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION;
}
void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value)
void GCodeProcessor::set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{
size_t id = static_cast<size_t>(mode);
if (id < m_time_processor.machines.size()) {
@ -3004,13 +3093,13 @@ void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mo
}
}
float GCodeProcessor::get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
float GCodeProcessor::get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
size_t id = static_cast<size_t>(mode);
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
}
void GCodeProcessor::set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value)
void GCodeProcessor::set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{
size_t id = static_cast<size_t>(mode);
if (id < m_time_processor.machines.size()) {
@ -3038,7 +3127,7 @@ float GCodeProcessor::get_filament_unload_time(size_t extruder_id)
void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
{
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i];
if (!machine.enabled)
continue;
@ -3055,17 +3144,26 @@ void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
}
}
void GCodeProcessor::process_filaments(CustomGCode::Type code)
{
if (code == CustomGCode::ColorChange)
m_used_filaments.process_color_change_cache();
if (code == CustomGCode::ToolChange)
m_used_filaments.process_extruder_cache(this);
}
void GCodeProcessor::simulate_st_synchronize(float additional_time)
{
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
m_time_processor.machines[i].simulate_st_synchronize(additional_time);
}
}
void GCodeProcessor::update_estimated_times_stats()
{
auto update_mode = [this](PrintEstimatedTimeStatistics::ETimeMode mode) {
PrintEstimatedTimeStatistics::Mode& data = m_result.time_statistics.modes[static_cast<size_t>(mode)];
auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast<size_t>(mode)];
data.time = get_time(mode);
data.custom_gcode_times = get_custom_gcode_times(mode, true);
data.moves_times = get_moves_time(mode);
@ -3073,11 +3171,15 @@ void GCodeProcessor::update_estimated_times_stats()
data.layers_times = get_layers_time(mode);
};
update_mode(PrintEstimatedTimeStatistics::ETimeMode::Normal);
if (m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled)
update_mode(PrintEstimatedTimeStatistics::ETimeMode::Stealth);
update_mode(PrintEstimatedStatistics::ETimeMode::Normal);
if (m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled)
update_mode(PrintEstimatedStatistics::ETimeMode::Stealth);
else
m_result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].reset();
m_result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].reset();
m_result.print_statistics.volumes_per_color_change = m_used_filaments.volumes_per_color_change;
m_result.print_statistics.volumes_per_extruder = m_used_filaments.volumes_per_extruder;
m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role;
}
} /* namespace Slic3r */

View file

@ -36,7 +36,7 @@ namespace Slic3r {
Count
};
struct PrintEstimatedTimeStatistics
struct PrintEstimatedStatistics
{
enum class ETimeMode : unsigned char
{
@ -62,14 +62,21 @@ namespace Slic3r {
}
};
std::vector<double> volumes_per_color_change;
std::map<size_t, double> volumes_per_extruder;
std::map<ExtrusionRole, std::pair<double, double>> used_filaments_per_role;
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
PrintEstimatedTimeStatistics() { reset(); }
PrintEstimatedStatistics() { reset(); }
void reset() {
for (auto m : modes) {
m.reset();
}
volumes_per_color_change.clear();
volumes_per_extruder.clear();
used_filaments_per_role.clear();
}
};
@ -314,7 +321,7 @@ namespace Slic3r {
// Additional load / unload times for a filament exchange sequence.
std::vector<float> filament_load_times;
std::vector<float> filament_unload_times;
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines;
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> machines;
void reset();
@ -327,6 +334,30 @@ namespace Slic3r {
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
};
struct UsedFilaments // filaments per ColorChange
{
double color_change_cache;
std::vector<double> volumes_per_color_change;
double tool_change_cache;
std::map<size_t, double> volumes_per_extruder;
double role_cache;
// ExtrusionRole : <used_filament_m, used_filament_g>
std::map<ExtrusionRole, std::pair<double, double>> filaments_per_role;
void reset();
void increase_caches(double extruded_volume);
void process_color_change_cache();
void process_extruder_cache(GCodeProcessor* processor);
void process_role_cache(GCodeProcessor* processor);
void process_caches(GCodeProcessor* processor);
friend class GCodeProcessor;
};
public:
#if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER
struct MoveVertex
@ -372,7 +403,7 @@ namespace Slic3r {
SettingsIds settings_ids;
size_t extruders_count;
std::vector<std::string> extruder_colors;
PrintEstimatedTimeStatistics time_statistics;
PrintEstimatedStatistics print_statistics;
#if ENABLE_GCODE_VIEWER_STATISTICS
int64_t time{ 0 };
@ -519,6 +550,7 @@ namespace Slic3r {
ExtruderColors m_extruder_colors;
ExtruderTemps m_extruder_temps;
std::vector<float> m_filament_diameters;
std::vector<float> m_filament_densities;
float m_extruded_last_z;
#if ENABLE_START_GCODE_VISUALIZATION
float m_first_layer_height; // mm
@ -550,6 +582,7 @@ namespace Slic3r {
bool m_producers_enabled;
TimeProcessor m_time_processor;
UsedFilaments m_used_filaments;
Result m_result;
static unsigned int s_result_id;
@ -566,7 +599,7 @@ namespace Slic3r {
void apply_config(const PrintConfig& config);
void enable_stealth_time_estimator(bool enabled);
bool is_stealth_time_estimator_enabled() const {
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled;
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
}
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
@ -579,13 +612,13 @@ namespace Slic3r {
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const;
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const;
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<float> get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const;
private:
void apply_config(const DynamicPrintConfig& config);
@ -701,20 +734,21 @@ namespace Slic3r {
void store_move_vertex(EMoveType type);
float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
void set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_filament_load_time(size_t extruder_id);
float get_filament_unload_time(size_t extruder_id);
void process_custom_gcode_time(CustomGCode::Type code);
void process_filaments(CustomGCode::Type code);
// Simulates firmware st_synchronize() call
void simulate_st_synchronize(float additional_time = 0.0f);

View file

@ -223,11 +223,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
layer_tools.extruder_override = extruder_override;
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions()[region_id];
for (const LayerRegion *layerm : layer->regions()) {
const PrintRegion &region = layerm->region();
if (! layerm->perimeters.entities.empty()) {
bool something_nonoverriddable = true;
@ -688,16 +685,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
for (const LayerRegion *layerm : this_layer->regions()) {
const auto &region = layerm->region();
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill;
if (print.config().infill_first != perimeters_done || wipe_into_infill_only) {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region))
@ -721,7 +716,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
// Now the same for perimeters - see comments above for explanation:
if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done)
{
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
for (const ExtrusionEntity* ee : layerm->perimeters.entities) {
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
set_extruder_override(fill, copy, new_extruder, num_of_copies);
@ -762,13 +757,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
size_t num_of_copies = object->instances().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
for (const LayerRegion *layerm : this_layer->regions()) {
const auto &region = layerm->region();
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region)
@ -791,7 +785,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
}
// Now the same for perimeters - see comments above for explanation:
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
for (const ExtrusionEntity* ee : layerm->perimeters.entities) { // iterate through all perimeter Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy))
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);

View file

@ -500,9 +500,9 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
ToolChangeResult result;
result.priming = priming;
result.initial_tool = int(old_tool);
result.new_tool = int(this->m_current_tool);
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.new_tool = int(m_current_tool);
result.print_z = m_z_pos;
result.layer_height = m_layer_height;
result.elapsed_time = writer.elapsed_time();
result.start_pos = writer.start_pos_rotated();
result.end_pos = priming ? writer.pos() : writer.pos_rotated();
@ -630,7 +630,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
bool /*last_wipe_inside_wipe_tower*/)
{
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
this->m_current_tool = tools.front();
m_current_tool = tools.front();
// The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
// Due to the XYZ calibration, this working space may shrink slightly from all directions,

View file

@ -164,10 +164,9 @@ public:
m_current_layer_finished = false;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
if (is_first_layer) {
this->m_num_layer_changes = 0;
this->m_num_tool_changes = 0;
}
else
m_num_layer_changes = 0;
m_num_tool_changes = 0;
} else
++ m_num_layer_changes;
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:

View file

@ -1083,8 +1083,7 @@ MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* pol
}
}
bool
MedialAxis::validate_edge(const VD::edge_type* edge)
bool MedialAxis::validate_edge(const VD::edge_type* edge)
{
// prevent overflows and detect almost-infinite edges
#ifndef CLIPPERLIB_INT32

View file

@ -27,7 +27,7 @@ bool Layer::empty() const
return true;
}
LayerRegion* Layer::add_region(PrintRegion* print_region)
LayerRegion* Layer::add_region(const PrintRegion *print_region)
{
m_regions.emplace_back(new LayerRegion(this, print_region));
return m_regions.back();
@ -102,7 +102,7 @@ ExPolygons Layer::merged(float offset_scaled) const
}
Polygons polygons;
for (LayerRegion *layerm : m_regions) {
const PrintRegionConfig &config = layerm->region()->config();
const PrintRegionConfig &config = layerm->region().config();
// Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty.
if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0)
append(polygons, offset(layerm->slices.surfaces, offset_scaled));
@ -134,7 +134,7 @@ void Layer::make_perimeters()
continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
done[region_id] = true;
const PrintRegionConfig &config = (*layerm)->region()->config();
const PrintRegionConfig &config = (*layerm)->region().config();
// find compatible regions
LayerRegionPtrs layerms;
@ -142,7 +142,7 @@ void Layer::make_perimeters()
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
if (! (*it)->slices.empty()) {
LayerRegion* other_layerm = *it;
const PrintRegionConfig &other_config = other_layerm->region()->config();
const PrintRegionConfig &other_config = other_layerm->region().config();
if (config.perimeter_extruder == other_config.perimeter_extruder
&& config.perimeters == other_config.perimeters
&& config.perimeter_speed == other_config.perimeter_speed
@ -180,7 +180,7 @@ void Layer::make_perimeters()
for (LayerRegion *layerm : layerms) {
for (Surface &surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface);
if (layerm->region()->config().fill_density > layerm_config->region()->config().fill_density)
if (layerm->region().config().fill_density > layerm_config->region().config().fill_density)
layerm_config = layerm;
}
// merge the surfaces assigned to each group

View file

@ -22,8 +22,7 @@ class LayerRegion
public:
Layer* layer() { return m_layer; }
const Layer* layer() const { return m_layer; }
PrintRegion* region() { return m_region; }
const PrintRegion* region() const { return m_region; }
const PrintRegion& region() const { return *m_region; }
// collection of surfaces generated by slicing the original geometry
// divided by type top/bottom/internal
@ -86,12 +85,12 @@ public:
protected:
friend class Layer;
LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {}
LayerRegion(Layer *layer, const PrintRegion *region) : m_layer(layer), m_region(region) {}
~LayerRegion() {}
private:
Layer *m_layer;
PrintRegion *m_region;
Layer *m_layer;
const PrintRegion *m_region;
};
@ -126,9 +125,9 @@ public:
std::vector<BoundingBox> lslices_bboxes;
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
LayerRegion* get_region(int idx) { return m_regions[idx]; }
LayerRegion* add_region(PrintRegion* print_region);
LayerRegion* add_region(const PrintRegion *print_region);
const LayerRegionPtrs& regions() const { return m_regions; }
// Test whether whether there are any slices assigned to this layer.
bool empty() const;

View file

@ -27,13 +27,14 @@ Flow LayerRegion::flow(FlowRole role, double layer_height) const
Flow LayerRegion::bridging_flow(FlowRole role) const
{
const PrintRegion &region = *this->region();
const PrintRegion &region = this->region();
const PrintRegionConfig &region_config = region.config();
if (this->layer()->object()->config().thick_bridges) {
const PrintObject &print_object = *this->layer()->object();
if (print_object.config().thick_bridges) {
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
// Applies default bridge spacing.
return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter);
} else {
@ -69,7 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
this->thin_fills.clear();
const PrintConfig &print_config = this->layer()->object()->print()->config();
const PrintRegionConfig &region_config = this->region()->config();
const PrintRegionConfig &region_config = this->region().config();
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
bool spiral_vase = print_config.spiral_vase &&
//FIXME account for raft layers.
@ -110,7 +111,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
{
const bool has_infill = this->region()->config().fill_density.value > 0.;
const bool has_infill = this->region().config().fill_density.value > 0.;
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -284,7 +285,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer %zu:\n", this->layer()->id());
#endif
double custom_angle = Geometry::deg2rad(this->region()->config().bridge_angle.value);
double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
if (bd.detect_angle(custom_angle)) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->has_support()) {
@ -383,21 +384,21 @@ void LayerRegion::prepare_fill_surfaces()
bool spiral_vase = this->layer()->object()->print()->config().spiral_vase;
// if no solid layers are requested, turn top/bottom surfaces to internal
if (! spiral_vase && this->region()->config().top_solid_layers == 0) {
if (! spiral_vase && this->region().config().top_solid_layers == 0) {
for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.is_top())
surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
}
if (this->region()->config().bottom_solid_layers == 0) {
if (this->region().config().bottom_solid_layers == 0) {
for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.is_bottom()) // (surface.surface_type == stBottom)
surface.surface_type = stInternal;
}
// turn too small internal regions into solid regions according to the user setting
if (! spiral_vase && this->region()->config().fill_density.value > 0) {
if (! spiral_vase && this->region().config().fill_density.value > 0) {
// scaling an area requires two calls!
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value));
double min_area = scale_(scale_(this->region().config().solid_infill_below_area.value));
for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.surface_type == stInternal && surface.area() <= min_area)
surface.surface_type = stInternalSolid;

View file

@ -942,30 +942,39 @@ void ModelObject::center_around_origin(bool include_modifiers)
{
// calculate the displacements needed to
// center this object around the origin
BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
// Shift is the vector from the center of the bounding box to the origin
Vec3d shift = -bb.center();
const Vec3d shift = -bb.center();
this->translate(shift);
this->origin_translation += shift;
}
#if ENABLE_ALLOW_NEGATIVE_Z
void ModelObject::ensure_on_bed(bool allow_negative_z)
{
const double min_z = get_min_z();
if (!allow_negative_z || min_z > 0.0)
translate_instances({ 0.0, 0.0, -min_z });
}
#else
void ModelObject::ensure_on_bed()
{
translate_instances(Vec3d(0.0, 0.0, -get_min_z()));
translate_instances({ 0.0, 0.0, -get_min_z() });
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
void ModelObject::translate_instances(const Vec3d& vector)
{
for (size_t i = 0; i < instances.size(); ++i)
{
for (size_t i = 0; i < instances.size(); ++i) {
translate_instance(i, vector);
}
}
void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
{
assert(instance_idx < instances.size());
ModelInstance* i = instances[instance_idx];
i->set_offset(i->get_offset() + vector);
invalidate_bounding_box();
@ -973,8 +982,7 @@ void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
void ModelObject::translate(double x, double y, double z)
{
for (ModelVolume *v : this->volumes)
{
for (ModelVolume *v : this->volumes) {
v->translate(x, y, z);
}
@ -984,8 +992,7 @@ void ModelObject::translate(double x, double y, double z)
void ModelObject::scale(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
for (ModelVolume *v : this->volumes) {
v->scale(versor);
}
this->invalidate_bounding_box();
@ -993,41 +1000,34 @@ void ModelObject::scale(const Vec3d &versor)
void ModelObject::rotate(double angle, Axis axis)
{
for (ModelVolume *v : this->volumes)
{
for (ModelVolume *v : this->volumes) {
v->rotate(angle, axis);
}
center_around_origin();
this->invalidate_bounding_box();
}
void ModelObject::rotate(double angle, const Vec3d& axis)
{
for (ModelVolume *v : this->volumes)
{
for (ModelVolume *v : this->volumes) {
v->rotate(angle, axis);
}
center_around_origin();
this->invalidate_bounding_box();
}
void ModelObject::mirror(Axis axis)
{
for (ModelVolume *v : this->volumes)
{
for (ModelVolume *v : this->volumes) {
v->mirror(axis);
}
this->invalidate_bounding_box();
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
for (ModelVolume *v : this->volumes) {
v->scale_geometry_after_creation(versor);
v->set_offset(versor.cwiseProduct(v->get_offset()));
}
@ -1418,11 +1418,9 @@ double ModelObject::get_min_z() const
{
if (instances.empty())
return 0.0;
else
{
else {
double min_z = DBL_MAX;
for (size_t i = 0; i < instances.size(); ++i)
{
for (size_t i = 0; i < instances.size(); ++i) {
min_z = std::min(min_z, get_instance_min_z(i));
}
return min_z;
@ -1433,15 +1431,14 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
{
double min_z = DBL_MAX;
ModelInstance* inst = instances[instance_idx];
const ModelInstance* inst = instances[instance_idx];
const Transform3d& mi = inst->get_matrix(true);
for (const ModelVolume* v : volumes)
{
for (const ModelVolume* v : volumes) {
if (!v->is_model_part())
continue;
Transform3d mv = mi * v->get_matrix();
const Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull();
for (const stl_facet &facet : hull.stl.facet_start)
for (int i = 0; i < 3; ++ i)
@ -1813,7 +1810,7 @@ void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_le
this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(mesh_trafo, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id();
}
@ -1825,7 +1822,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(matrix, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id();
}

View file

@ -180,8 +180,8 @@ private:
class LayerHeightProfile final : public ObjectWithTimestamp {
public:
// Assign the content if the timestamp differs, don't assign an ObjectID.
void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
std::vector<coordf_t> get() const throw() { return m_data; }
bool empty() const throw() { return m_data.empty(); }
@ -306,7 +306,11 @@ public:
void center_around_origin(bool include_modifiers = true);
#if ENABLE_ALLOW_NEGATIVE_Z
void ensure_on_bed(bool allow_negative_z = false);
#else
void ensure_on_bed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
void translate_instances(const Vec3d& vector);
void translate_instance(size_t instance_idx, const Vec3d& vector);
void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
@ -504,8 +508,8 @@ enum class ConversionType : int {
class FacetsAnnotation final : public ObjectWithTimestamp {
public:
// Assign the content if the timestamp differs, don't assign an ObjectID.
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
const std::map<int, std::vector<bool>>& get_data() const throw() { return m_data; }
bool set(const TriangleSelector& selector);
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
@ -680,6 +684,7 @@ protected:
friend class SLAPrint;
friend class Model;
friend class ModelObject;
friend void model_volume_list_update_supports(ModelObject& model_object_dst, const ModelObject& model_object_new);
// Copies IDs of both the ModelVolume and its config.
explicit ModelVolume(const ModelVolume &rhs) = default;

View file

@ -52,7 +52,7 @@ public:
PointType* operator->() const { return &m_data->at(m_idx).point; }
MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
IndexType size() const { assert(this->valid()); return m_data->size(); }
iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; }
iterator& remove() { m_idx = m_data->remove(*this).m_idx; return *this; }
iterator insert(const PointType pt) const { return m_data->insert(*this, pt); }
private:
iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
@ -162,10 +162,10 @@ public:
return out;
};
bool empty() const { return this->m_size == 0; }
size_t size() const { return this->m_size; }
size_t capacity() const { return this->m_data.capacity(); }
bool valid() const { return this->m_size >= 3; }
bool empty() const { return m_size == 0; }
size_t size() const { return m_size; }
size_t capacity() const { return m_data.capacity(); }
bool valid() const { return m_size >= 3; }
void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); }
iterator begin() { return { this, m_head }; }

View file

@ -121,8 +121,8 @@ protected:
if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff);
if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval);
if(this->m_stopcr.max_iterations() > 0)
nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations());
if(m_stopcr.max_iterations() > 0)
nlopt_set_maxeval(nl.ptr, m_stopcr.max_iterations());
}
template<class Fn, size_t N>

View file

@ -103,6 +103,7 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
// Based on https://www.lemoda.net/c/write-png/
// png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY
//FIXME maybe better to use tdefl_write_image_to_png_file_in_memory() instead?
static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data)
{
bool result = false;

View file

@ -33,32 +33,16 @@ Polyline Polygon::split_at_index(int index) const
return polyline;
}
/*
int64_t Polygon::area2x() const
{
size_t n = poly.size();
if (n < 3)
return 0;
int64_t a = 0;
for (size_t i = 0, j = n - 1; i < n; ++i)
a += int64_t(poly[j](0) + poly[i](0)) * int64_t(poly[j](1) - poly[i](1));
j = i;
}
return -a * 0.5;
}
*/
double Polygon::area(const Points &points)
{
size_t n = points.size();
if (n < 3)
return 0.;
double a = 0.;
for (size_t i = 0, j = n - 1; i < n; ++i) {
a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1));
j = i;
if (points.size() >= 3) {
Vec2d p1 = points.back().cast<double>();
for (const Point &p : points) {
Vec2d p2 = p.cast<double>();
a += cross2(p1, p2);
p1 = p2;
}
}
return 0.5 * a;
}
@ -169,19 +153,22 @@ void Polygon::triangulate_convex(Polygons* polygons) const
}
// center of mass
// source: https://en.wikipedia.org/wiki/Centroid
Point Polygon::centroid() const
{
double area_temp = this->area();
double x_temp = 0;
double y_temp = 0;
Polyline polyline = this->split_at_first_point();
for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) {
x_temp += (double)( point->x() + (point+1)->x() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() );
y_temp += (double)( point->y() + (point+1)->y() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() );
double area_sum = 0.;
Vec2d c(0., 0.);
if (points.size() >= 3) {
Vec2d p1 = points.back().cast<double>();
for (const Point &p : points) {
Vec2d p2 = p.cast<double>();
double a = cross2(p1, p2);
area_sum += a;
c += (p1 + p2) * a;
p1 = p2;
}
}
return Point(x_temp/(6*area_temp), y_temp/(6*area_temp));
return Point(Vec2d(c / (3. * area_sum)));
}
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)

View file

@ -78,6 +78,9 @@ public:
bool is_closed() const { return this->points.front() == this->points.back(); }
};
inline bool operator==(const Polyline &lhs, const Polyline &rhs) { return lhs.points == rhs.points; }
inline bool operator!=(const Polyline &lhs, const Polyline &rhs) { return lhs.points != rhs.points; }
// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
#ifdef PERL_UCHAR_MIN
class PolylineCollection

View file

@ -624,11 +624,17 @@ const std::vector<std::string>& Preset::sla_printer_options()
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
m_type(type),
m_edited_preset(type, "", false),
#if ENABLE_PROJECT_DIRTY_STATE
m_saved_preset(type, "", false),
#endif // ENABLE_PROJECT_DIRTY_STATE
m_idx_selected(0)
{
// Insert just the default preset.
this->add_default_preset(keys, defaults, default_name);
m_edited_preset.config.apply(m_presets.front().config);
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void PresetCollection::reset(bool delete_files)
@ -805,7 +811,10 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
this->get_edited_preset().config.apply_only(combined_config, keys, true);
this->update_dirty();
assert(this->get_edited_preset().is_dirty);
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
assert(this->get_edited_preset().is_dirty);
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
}
if (inherits.empty()) {
@ -1070,7 +1079,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
size_t PresetCollection::first_visible_idx() const
{
size_t idx = m_default_suppressed ? m_num_default_presets : 0;
for (; idx < this->m_presets.size(); ++ idx)
for (; idx < m_presets.size(); ++ idx)
if (m_presets[idx].is_visible)
break;
if (idx == m_presets.size())
@ -1215,6 +1224,9 @@ Preset& PresetCollection::select_preset(size_t idx)
idx = first_visible_idx();
m_idx_selected = idx;
m_edited_preset = m_presets[idx];
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible;
@ -1282,7 +1294,7 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
assert(it != new_vendors.end());
preset.vendor = &it->second;
}
this->m_presets.emplace(it, std::move(preset));
m_presets.emplace(it, std::move(preset));
} else
duplicates.emplace_back(std::move(preset.name));
}

View file

@ -346,6 +346,11 @@ public:
Preset& get_edited_preset() { return m_edited_preset; }
const Preset& get_edited_preset() const { return m_edited_preset; }
#if ENABLE_PROJECT_DIRTY_STATE
// Return the last saved preset.
const Preset& get_saved_preset() const { return m_saved_preset; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
@ -365,8 +370,16 @@ public:
// Return a preset by an index. If the preset is active, a temporary copy is returned.
Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); }
#if ENABLE_PROJECT_DIRTY_STATE
void discard_current_changes() {
m_presets[m_idx_selected].reset_dirty();
m_edited_preset = m_presets[m_idx_selected];
update_saved_preset_from_current_preset();
}
#else
void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned.
Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false);
@ -440,6 +453,16 @@ public:
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
#if ENABLE_PROJECT_DIRTY_STATE
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> saved_dirty_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
// Copy edited preset into saved preset.
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return a sorted list of system preset names.
// Used for validating the "inherits" flag when importing user's config bundles.
// Returns names of all system presets including the former names of these presets.
@ -527,6 +550,11 @@ private:
std::map<std::string, std::string> m_map_system_profile_renamed;
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
Preset m_edited_preset;
#if ENABLE_PROJECT_DIRTY_STATE
// Contains a copy of the last saved selected preset.
Preset m_saved_preset;
#endif // ENABLE_PROJECT_DIRTY_STATE
// Selected preset.
size_t m_idx_selected;
// Is the "- default -" preset suppressed?

View file

@ -31,6 +31,9 @@ namespace Slic3r {
template class PrintState<PrintStep, psCount>;
template class PrintState<PrintObjectStep, posCount>;
PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {}
PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {}
void Print::clear()
{
tbb::mutex::scoped_lock lock(this->state_mutex());
@ -39,24 +42,10 @@ void Print::clear()
for (PrintObject *object : m_objects)
delete object;
m_objects.clear();
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
m_print_regions.clear();
m_model.clear_objects();
}
PrintRegion* Print::add_region()
{
m_regions.emplace_back(new PrintRegion(this));
return m_regions.back();
}
PrintRegion* Print::add_region(const PrintRegionConfig &config)
{
m_regions.emplace_back(new PrintRegion(this, config));
return m_regions.back();
}
// Called by Print::apply().
// This method only accepts PrintConfig option keys.
bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector<t_config_option_key> &opt_keys)
@ -273,15 +262,10 @@ bool Print::is_step_done(PrintObjectStep step) const
std::vector<unsigned int> Print::object_extruders() const
{
std::vector<unsigned int> extruders;
extruders.reserve(m_regions.size() * 3);
std::vector<unsigned char> region_used(m_regions.size(), false);
extruders.reserve(m_print_regions.size() * m_objects.size() * 3);
for (const PrintObject *object : m_objects)
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : object->region_volumes)
if (! volumes_per_region.empty())
region_used[&volumes_per_region - &object->region_volumes.front()] = true;
for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region)
if (region_used[idx_region])
m_regions[idx_region]->collect_object_printing_extruders(extruders);
for (const PrintRegion &region : object->all_regions())
region.collect_object_printing_extruders(*this, extruders);
sort_remove_duplicates(extruders);
return extruders;
}
@ -345,242 +329,6 @@ double Print::max_allowed_layer_height() const
return nozzle_diameter_max;
}
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
// in the exact order and with the same IDs.
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new)
{
typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
std::vector<ModelVolumeWithStatus> old_volumes;
old_volumes.reserve(model_object_dst.volumes.size());
for (const ModelVolume *model_volume : model_object_dst.volumes)
old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
model_object_dst.volumes.clear();
model_object_dst.volumes.reserve(model_object_new.volumes.size());
for (const ModelVolume *model_volume_src : model_object_new.volumes) {
ModelVolumeWithStatus key(model_volume_src, false);
auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
if (it != old_volumes.end() && model_volume_equal(*it, key)) {
// The volume was found in the old list. Just copy it.
assert(! it->second); // not consumed yet
it->second = true;
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
model_object_dst.volumes.emplace_back(model_volume_dst);
if (model_volume_dst->is_support_modifier()) {
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
model_volume_dst->set_type(model_volume_src->type());
model_volume_dst->set_transformation(model_volume_src->get_transformation());
}
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
} else {
// The volume was not found in the old list. Create a new copy.
assert(model_volume_src->is_support_modifier());
model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
}
}
// Release the non-consumed old volumes (those were deleted from the new list).
for (ModelVolumeWithStatus &mv_with_status : old_volumes)
if (! mv_with_status.second)
delete mv_with_status.first;
}
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
{
size_t i_src, i_dst;
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
const ModelVolume &mv_src = *model_object_src.volumes[i_src];
ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
if (mv_src.type() != type) {
++ i_src;
continue;
}
if (mv_dst.type() != type) {
++ i_dst;
continue;
}
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
mv_dst.config.assign_config(mv_src.config);
assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
mv_dst.supported_facets.assign(mv_src.supported_facets);
assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
mv_dst.seam_facets.assign(mv_src.seam_facets);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
++ i_dst;
}
}
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
{
assert(lr_dst.size() == lr_src.size());
auto it_src = lr_src.cbegin();
for (auto &kvp_dst : lr_dst) {
const auto &kvp_src = *it_src ++;
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
kvp_dst.second = kvp_src.second;
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
if (*lv < *rv)
return true;
else if (*lv > *rv)
return false;
}
return false;
}
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
if (*lv != *rv)
return false;
return true;
}
struct PrintObjectTrafoAndInstances
{
Transform3d trafo;
PrintInstances instances;
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
{
std::set<PrintObjectTrafoAndInstances> trafos;
PrintObjectTrafoAndInstances trafo;
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix();
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
// Reset the XY axes of the transformation.
trafo.trafo.data()[12] = 0;
trafo.trafo.data()[13] = 0;
// Search or insert a trafo.
auto it = trafos.emplace(trafo).first;
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
}
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
}
// Compare just the layer ranges and their layer heights, not the associated configs.
// Ignore the layer heights if check_layer_heights is false.
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
{
if (lr1.size() != lr2.size())
return false;
auto it2 = lr2.begin();
for (const auto &kvp1 : lr1) {
const auto &kvp2 = *it2 ++;
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
return false;
}
return true;
}
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
{
auto it_a = va.begin();
auto it_b = vb.begin();
while (it_a != va.end() || it_b != vb.end()) {
if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_a;
continue;
}
if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_b;
continue;
}
if (it_a == va.end() || it_b == vb.end())
// va or vb contains more Tool Changes than the other.
return true;
assert(it_a->type == CustomGCode::ToolChange);
assert(it_b->type == CustomGCode::ToolChange);
if (*it_a != *it_b)
// The two Tool Changes differ.
return true;
++ it_a;
++ it_b;
}
// There is no change in custom Tool Changes.
return false;
}
// Collect diffs of configuration values at various containers,
// resolve the filament rectract overrides of extruder retract values.
void Print::config_diffs(
const DynamicPrintConfig &new_full_config,
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
t_config_option_keys &full_config_diff,
DynamicPrintConfig &filament_overrides) const
{
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
{
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_";
for (const t_config_option_key &opt_key : m_config.keys()) {
const ConfigOption *opt_old = m_config.option(opt_key);
assert(opt_old != nullptr);
const ConfigOption *opt_new = new_full_config.option(opt_key);
// assert(opt_new != nullptr);
if (opt_new == nullptr)
//FIXME This may happen when executing some test cases.
continue;
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
auto opt_copy = opt_new->clone();
opt_copy->apply_override(opt_new_filament);
if (*opt_old == *opt_copy)
delete opt_copy;
else {
filament_overrides.set_key_value(opt_key, opt_copy);
print_diff.emplace_back(opt_key);
}
}
} else if (*opt_new != *opt_old)
print_diff.emplace_back(opt_key);
}
}
// Collect changes to object and region configs.
object_diff = m_default_object_config.diff(new_full_config);
region_diff = m_default_region_config.diff(new_full_config);
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
for (const t_config_option_key &opt_key : new_full_config.keys()) {
const ConfigOption *opt_old = m_full_print_config.option(opt_key);
const ConfigOption *opt_new = new_full_config.option(opt_key);
if (opt_old == nullptr || *opt_new != *opt_old)
full_config_diff.emplace_back(opt_key);
}
}
std::vector<ObjectID> Print::print_object_ids() const
{
std::vector<ObjectID> out;
@ -591,594 +339,6 @@ std::vector<ObjectID> Print::print_object_ids() const
return out;
}
Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
// Normalize the config.
new_full_config.option("print_settings_id", true);
new_full_config.option("filament_settings_id", true);
new_full_config.option("printer_settings_id", true);
new_full_config.option("physical_printer_settings_id", true);
new_full_config.normalize_fdm();
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
DynamicPrintConfig filament_overrides;
this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides);
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated)
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
update_apply_status(false);
// Grab the lock for the Print / PrintObject milestones.
tbb::mutex::scoped_lock lock(this->state_mutex());
// The following call may stop the background processing.
if (! print_diff.empty())
update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty.
size_t num_extruders = m_config.nozzle_diameter.size();
bool num_extruders_changed = false;
if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
// see "Placeholders do not respect filament overrides." GH issue #3649
m_placeholder_parser.apply_config(filament_overrides);
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, print_diff, true);
//FIXME use move semantics once ConfigBase supports it.
m_config.apply(filament_overrides);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, object_diff, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
}
}
class LayerRanges
{
public:
LayerRanges() {}
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
void assign(const t_layer_config_ranges &in) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig *cfg = &range.second.get();
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR
// assert(it != m_ranges.end());
// assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
// assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
if (it == m_ranges.end() ||
std::abs(it->first.first - range.first) > EPSILON ||
std::abs(it->first.second - range.second) > EPSILON )
return nullptr; // desired range doesn't found
return (it == m_ranges.end()) ? nullptr : it->second;
}
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
private:
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
};
struct ModelObjectStatus {
enum Status {
Unknown,
Old,
New,
Moved,
Deleted,
};
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
std::set<ModelObjectStatus> model_object_status;
// 1) Synchronize model objects.
if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch.
// Stop background processing.
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
for (PrintObject *object : m_objects) {
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
update_apply_status(object->invalidate_all_steps());
delete object;
}
m_objects.clear();
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport));
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
m_model.objects.back()->set_model(&m_model);
}
} else {
// Reorder the objects, add new objects.
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
this->call_cancel_callback();
update_apply_status(this->invalidate_step(psGCodeExport));
// Second create a new list of objects.
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
m_model.objects.clear();
m_model.objects.reserve(model.objects.size());
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
for (const ModelObject *mobj : model.objects) {
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
// New ModelObject added.
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
m_model.objects.back()->set_model(&m_model);
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
} else {
// Existing ModelObject re-added (possibly moved in the list).
m_model.objects.emplace_back(*it);
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
}
}
bool deleted_any = false;
for (ModelObject *&model_object : model_objects_old) {
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
deleted_any = true;
} else
// Do not delete this ModelObject instance.
model_object = nullptr;
}
if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects.
PrintObjectPtrs print_objects_old = std::move(m_objects);
m_objects.clear();
m_objects.reserve(print_objects_old.size());
for (PrintObject *print_object : print_objects_old) {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
if (it_status->status == ModelObjectStatus::Deleted) {
update_apply_status(print_object->invalidate_all_steps());
delete print_object;
} else
m_objects.emplace_back(print_object);
}
for (ModelObject *model_object : model_objects_old)
delete model_object;
}
}
}
// 2) Map print objects including their transformation matrices.
struct PrintObjectStatus {
enum Status {
Unknown,
Deleted,
Reused,
New
};
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
id(print_object->model_object()->id()),
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
Transform3d trafo;
Status status;
// Search by id.
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
};
std::multiset<PrintObjectStatus> print_object_status;
for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object));
// 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object];
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object];
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
update_apply_status(it->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
if (supports_differ) {
this->call_cancel_callback();
update_apply_status(false);
}
// Invalidate just the supports step.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it)
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
if (supports_differ) {
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
}
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
update_apply_status(this->invalidate_step(psGCodeExport));
}
if (! model_parts_differ && ! modifiers_differ) {
// Synchronize Object's config.
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
if (object_config_changed)
model_object.config.assign_config(model_object_new.config);
if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
t_config_option_keys diff = it->print_object->config().diff(new_config);
if (! diff.empty()) {
update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff));
it->print_object->config_apply_only(new_config, diff, true);
}
}
}
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
model_object.invalidate_bounding_box();
// Synchronize the content of instances.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
(*old_instance)->printable = (*new_instance)->printable;
}
}
}
}
// 4) Generate PrintObjects from ModelObjects and their instances.
{
PrintObjectPtrs print_objects_new;
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
bool new_objects = false;
// Walk over all new model objects and check, whether there are matching PrintObjects.
for (ModelObject *model_object : m_model.objects) {
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
std::vector<const PrintObjectStatus*> old;
if (range.first != range.second) {
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
for (auto it = range.first; it != range.second; ++ it)
if (it->status != PrintObjectStatus::Deleted)
old.emplace_back(&(*it));
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
// Producing the config for PrintObject on demand, caching it at print_object_last.
const PrintObject *print_object_last = nullptr;
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
print_object->config_apply(print_object_last ?
print_object_last->config() :
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
print_object_last = print_object;
};
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) {
// Simple case, just generate new instances.
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
}
continue;
}
// Complex case, try to merge the two lists.
// Sort the old lexicographically by their trafos.
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists.
auto it_old = old.begin();
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
if (it_old != old.end())
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
} else {
// The PrintObject already exists and the copies differ.
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
print_objects_new.emplace_back((*it_old)->print_object);
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
}
}
}
if (m_objects != print_objects_new) {
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
deleted_objects = true;
}
if (new_objects || deleted_objects)
update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
if (new_objects)
update_apply_status(false);
}
print_object_status.clear();
}
// 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions.
// Update reference counts of regions from the remaining PrintObjects and their volumes.
// Regions with zero references could and should be reused.
for (PrintRegion *region : m_regions)
region->m_refcnt = 0;
for (PrintObject *print_object : m_objects) {
int idx_region = 0;
for (const auto &volumes : print_object->region_volumes) {
if (! volumes.empty())
++ m_regions[idx_region]->m_refcnt;
++ idx_region;
}
}
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
PrintRegion &region = *m_regions[region_id];
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) {
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
if (region_id < print_object->region_volumes.size()) {
for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
// Regions were split. Reset this print_object.
goto print_object_end;
} else {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *m_regions[i];
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
// Regions were merged. Reset this print_object.
goto print_object_end;
}
this_region_config_set = true;
}
}
}
continue;
print_object_end:
update_apply_status(print_object->invalidate_all_steps());
// Decrease the references to regions from this volume.
int ireg = 0;
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
if (! volumes.empty())
-- m_regions[ireg]->m_refcnt;
++ ireg;
}
print_object->region_volumes.clear();
}
if (this_region_config_set) {
t_config_option_keys diff = region.config().diff(this_region_config);
if (! diff.empty()) {
// Stop the background process before assigning new configuration to the regions.
for (PrintObject *print_object : m_objects)
if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty())
update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff));
region.config_apply_only(this_region_config, diff, false);
}
}
}
// Possibly add new regions for the newly added or resetted PrintObjects.
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_object();
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
std::vector<int> regions_in_object;
regions_in_object.reserve(64);
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
PrintObject &print_object = *m_objects[i];
bool fresh = print_object.region_volumes.empty();
unsigned int volume_id = 0;
unsigned int idx_region_in_object = 0;
for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id;
continue;
}
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
// Now insert a volume with a layer range to its own region.
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
int region_id = -1;
if (&print_object == &print_object0) {
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
// Find an existing print region with the same config.
int idx_empty_slot = -1;
for (int i = 0; i < (int)m_regions.size(); ++ i) {
if (m_regions[i]->m_refcnt == 0) {
if (idx_empty_slot == -1)
idx_empty_slot = i;
} else if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
}
}
// If no region exists with the same config, create a new one.
if (region_id == -1) {
if (idx_empty_slot == -1) {
region_id = (int)m_regions.size();
this->add_region(config);
} else {
region_id = idx_empty_slot;
m_regions[region_id]->set_config(std::move(config));
}
}
regions_in_object.emplace_back(region_id);
} else
region_id = regions_in_object[idx_region_in_object ++];
// Assign volume to a region.
if (fresh) {
if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
++ m_regions[region_id]->m_refcnt;
print_object.add_region_volume(region_id, volume_id, it_range->first);
}
}
++ volume_id;
}
}
}
// 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 */
return static_cast<ApplyStatus>(apply_status);
}
bool Print::has_infinite_skirt() const
{
return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
@ -1289,10 +449,12 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
std::string Print::validate(std::string* warning) const
{
std::vector<unsigned int> extruders = this->extruders();
if (m_objects.empty())
return L("All objects are outside of the print volume.");
if (extruders().empty())
if (extruders.empty())
return L("The supplied settings will cause an empty print.");
if (m_config.complete_objects) {
@ -1311,20 +473,16 @@ std::string Print::validate(std::string* warning) const
return L("Only a single object may be printed at a time in Spiral Vase mode. "
"Either remove all but the last object, or enable sequential mode by \"complete_objects\".");
assert(m_objects.size() == 1);
size_t num_regions = 0;
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : m_objects.front()->region_volumes)
if (! volumes_per_region.empty())
++ num_regions;
if (num_regions > 1)
if (m_objects.front()->all_regions().size() > 1)
return L("The Spiral Vase option can only be used when printing single material objects.");
}
if (this->has_wipe_tower() && ! m_objects.empty()) {
// Make sure all extruders use same diameter filament and have the same nozzle diameter
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders().front());
double first_filament_diam = m_config.filament_diameter.get_at(extruders().front());
for (const auto& extruder_idx : extruders()) {
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
for (const auto& extruder_idx : extruders) {
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
@ -1342,7 +500,7 @@ std::string Print::validate(std::string* warning) const
return L("Ooze prevention is currently not supported with the wipe tower enabled.");
if (m_config.use_volumetric_e)
return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).");
if (m_config.complete_objects && extruders().size() > 1)
if (m_config.complete_objects && extruders.size() > 1)
return L("The Wipe Tower is currently not supported for multimaterial sequential prints.");
if (m_objects.size() > 1) {
@ -1422,8 +580,6 @@ std::string Print::validate(std::string* warning) const
}
{
std::vector<unsigned int> extruders = this->extruders();
// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
double min_nozzle_diameter = std::numeric_limits<double>::max();
double max_nozzle_diameter = 0;
@ -1530,8 +686,8 @@ std::string Print::validate(std::string* warning) const
if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg))
return err_msg;
for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" })
for (size_t i = 0; i < object->region_volumes.size(); ++ i)
if (! object->region_volumes[i].empty() && ! validate_extrusion_width(this->get_region(i)->config(), opt_key, layer_height, err_msg))
for (const PrintRegion &region : object->all_regions())
if (! validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
return err_msg;
}
}
@ -1606,7 +762,7 @@ Flow Print::brim_flow() const
{
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
if (width.value == 0)
width = m_regions.front()->config().perimeter_extrusion_width;
width = m_print_regions.front()->config().perimeter_extrusion_width;
if (width.value == 0)
width = m_objects.front()->config().extrusion_width;
@ -1618,7 +774,7 @@ Flow Print::brim_flow() const
return Flow::new_from_config_width(
frPerimeter,
width,
(float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().perimeter_extruder-1),
(float)this->skirt_first_layer_height());
}
@ -1626,7 +782,7 @@ Flow Print::skirt_flow() const
{
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
if (width.value == 0)
width = m_regions.front()->config().perimeter_extrusion_width;
width = m_print_regions.front()->config().perimeter_extrusion_width;
if (width.value == 0)
width = m_objects.front()->config().extrusion_width;

View file

@ -15,6 +15,9 @@
#include "libslic3r.h"
#include <functional>
#include <set>
namespace Slic3r {
class Print;
@ -57,12 +60,20 @@ enum PrintObjectStep {
// sharing the same config (including the same assigned extruder(s))
class PrintRegion
{
friend class Print;
public:
PrintRegion() = default;
PrintRegion(const PrintRegionConfig &config);
PrintRegion(const PrintRegionConfig &config, const size_t config_hash) : m_config(config), m_config_hash(config_hash) {}
PrintRegion(PrintRegionConfig &&config);
PrintRegion(PrintRegionConfig &&config, const size_t config_hash) : m_config(std::move(config)), m_config_hash(config_hash) {}
~PrintRegion() = default;
// Methods NOT modifying the PrintRegion's state:
public:
const Print* print() const { return m_print; }
const PrintRegionConfig& config() const { return m_config; }
const PrintRegionConfig& config() const throw() { return m_config; }
size_t config_hash() const throw() { return m_config_hash; }
// Identifier of this PrintRegion in the list of Print::m_print_regions.
int print_region_id() const throw() { return m_print_region_id; }
// 1-based extruder identifier for this region and role.
unsigned int extruder(FlowRole role) const;
Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const;
@ -72,29 +83,25 @@ public:
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Collect 0-based extruder indices used to print this region's object.
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
void collect_object_printing_extruders(const Print &print, std::vector<unsigned int> &object_extruders) const;
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, const bool has_brim, std::vector<unsigned int> &object_extruders);
// Methods modifying the PrintRegion's state:
public:
Print* print() { return m_print; }
void set_config(const PrintRegionConfig &config) { m_config = config; }
void set_config(PrintRegionConfig &&config) { m_config = std::move(config); }
void set_config(const PrintRegionConfig &config) { m_config = config; m_config_hash = m_config.hash(); }
void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
protected:
size_t m_refcnt;
{ m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); }
private:
Print *m_print;
friend Print;
PrintRegionConfig m_config;
PrintRegion(Print* print) : m_refcnt(0), m_print(print) {}
PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
~PrintRegion() = default;
size_t m_config_hash;
int m_print_region_id = -1;
};
inline bool operator==(const PrintRegion &lhs, const PrintRegion &rhs) { return lhs.config_hash() == rhs.config_hash() && lhs.config() == rhs.config(); }
inline bool operator!=(const PrintRegion &lhs, const PrintRegion &rhs) { return ! (lhs == rhs); }
template<typename T>
class ConstVectorOfPtrsAdaptor {
public:
@ -145,15 +152,40 @@ struct PrintInstance
typedef std::vector<PrintInstance> PrintInstances;
// Region and its volumes (printing volumes or modifier volumes)
struct PrintRegionVolumes
{
// Single volume + Z range assigned to a region.
struct VolumeWithZRange {
// Z range to slice this ModelVolume over.
t_layer_height_range layer_height_range;
// Index of a ModelVolume inside its parent ModelObject.
int volume_idx;
};
// Overriding one region with some other extruder, producing another region.
// The region is owned by PrintObject::m_all_regions.
struct ExtruderOverride {
unsigned int extruder;
// const PrintRegion *region;
};
// The region is owned by PrintObject::m_all_regions.
// const PrintRegion *region;
// Possible overrides of the default region extruder.
std::vector<ExtruderOverride> overrides;
// List of ModelVolume indices and layer ranges of thereof.
std::vector<VolumeWithZRange> volumes;
// Is this region printing in any layer?
// bool printing { false };
};
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
{
private: // Prevents erroneous use by other classes.
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
public:
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
const Vec3crd& size() const { return m_size; }
const PrintObjectConfig& config() const { return m_config; }
@ -180,9 +212,9 @@ public:
// adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
if (region_id >= region_volumes.size())
region_volumes.resize(region_id + 1);
region_volumes[region_id].emplace_back(layer_range, volume_id);
if (region_id >= m_region_volumes.size())
m_region_volumes.resize(region_id + 1);
m_region_volumes[region_id].volumes.push_back({ layer_range, volume_id });
}
// This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id
@ -213,7 +245,7 @@ public:
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
// Returns true, if the layer_height_profile was changed.
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
// by the interactive layer height editor and by the printing process itself.
@ -222,6 +254,11 @@ public:
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
size_t num_printing_regions() const throw() { return m_all_regions.size(); }
const PrintRegion& printing_region(size_t idx) const throw() { return *m_all_regions[idx]; }
//FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end.
std::vector<std::reference_wrapper<const PrintRegion>> all_regions() const;
bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; }
bool has_raft() const { return m_config.raft_layers > 0; }
bool has_support_material() const { return this->has_support() || this->has_raft(); }
@ -247,8 +284,8 @@ private:
PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
~PrintObject() = default;
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); }
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
// Invalidates the step, and its depending steps in PrintObject and Print.
bool invalidate_step(PrintObjectStep step);
@ -296,6 +333,10 @@ private:
// This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
Point m_center_offset;
std::vector<std::unique_ptr<PrintRegion>> m_all_regions;
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<PrintRegionVolumes> m_region_volumes;
SlicingParameters m_slicing_params;
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
@ -366,7 +407,7 @@ struct PrintStatistics
double total_weight;
double total_wipe_tower_cost;
double total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
std::map<size_t, double> filament_stats;
// Config with the filled in print statistics.
DynamicConfig config() const;
@ -395,11 +436,13 @@ class ConstPrintObjectPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintObject>
};
typedef std::vector<PrintRegion*> PrintRegionPtrs;
/*
typedef std::vector<const PrintRegion*> ConstPrintRegionPtrs;
class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintRegion> {
friend Print;
ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor<PrintRegion>(data) {}
};
*/
// The complete print tray with possibly multiple objects.
class Print : public PrintBaseWithState<PrintStep, psCount>
@ -470,14 +513,14 @@ public:
[object_id](const PrintObject *obj) { return obj->id() == object_id; });
return (it == m_objects.end()) ? nullptr : *it;
}
ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); }
// ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); }
// How many of PrintObject::copies() over all print objects are there?
// If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const;
// For Perl bindings.
PrintObjectPtrs& objects_mutable() { return m_objects; }
PrintRegionPtrs& regions_mutable() { return m_regions; }
PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; }
@ -498,30 +541,19 @@ public:
std::string output_filename(const std::string &filename_base = std::string()) const override;
// Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing
size_t num_print_regions() const throw() { return m_print_regions.size(); }
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
#if ENABLE_SEQUENTIAL_LIMITS
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
#endif // ENABLE_SEQUENTIAL_LIMITS
protected:
// methods for handling regions
PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
PrintRegion* add_region();
PrintRegion* add_region(const PrintRegionConfig &config);
// Invalidates the step, and its depending steps in Print.
bool invalidate_step(PrintStep step);
private:
void config_diffs(
const DynamicPrintConfig &new_full_config,
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
t_config_option_keys &full_config_diff,
DynamicPrintConfig &filament_overrides) const;
bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector<t_config_option_key> &opt_keys);
void _make_skirt();
@ -533,14 +565,11 @@ private:
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners() const;
// Declared here to have access to Model / ModelObject / ModelInstance
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;
PrintObjectPtrs m_objects;
PrintRegionPtrs m_regions;
PrintRegionPtrs m_print_regions;
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;

View file

@ -0,0 +1,824 @@
#include "Model.hpp"
#include "Print.hpp"
namespace Slic3r {
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
// in the exact order and with the same IDs.
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
// Friend to ModelVolume to allow copying.
// static is not accepted by gcc if declared as a friend of ModelObject.
/* static */ void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new)
{
typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
std::vector<ModelVolumeWithStatus> old_volumes;
old_volumes.reserve(model_object_dst.volumes.size());
for (const ModelVolume *model_volume : model_object_dst.volumes)
old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
model_object_dst.volumes.clear();
model_object_dst.volumes.reserve(model_object_new.volumes.size());
for (const ModelVolume *model_volume_src : model_object_new.volumes) {
ModelVolumeWithStatus key(model_volume_src, false);
auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
if (it != old_volumes.end() && model_volume_equal(*it, key)) {
// The volume was found in the old list. Just copy it.
assert(! it->second); // not consumed yet
it->second = true;
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
model_object_dst.volumes.emplace_back(model_volume_dst);
if (model_volume_dst->is_support_modifier()) {
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
model_volume_dst->set_type(model_volume_src->type());
model_volume_dst->set_transformation(model_volume_src->get_transformation());
}
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
} else {
// The volume was not found in the old list. Create a new copy.
assert(model_volume_src->is_support_modifier());
model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
}
}
// Release the non-consumed old volumes (those were deleted from the new list).
for (ModelVolumeWithStatus &mv_with_status : old_volumes)
if (! mv_with_status.second)
delete mv_with_status.first;
}
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
{
size_t i_src, i_dst;
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
const ModelVolume &mv_src = *model_object_src.volumes[i_src];
ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
if (mv_src.type() != type) {
++ i_src;
continue;
}
if (mv_dst.type() != type) {
++ i_dst;
continue;
}
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
mv_dst.config.assign_config(mv_src.config);
assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
mv_dst.supported_facets.assign(mv_src.supported_facets);
assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
mv_dst.seam_facets.assign(mv_src.seam_facets);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
++ i_dst;
}
}
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
{
assert(lr_dst.size() == lr_src.size());
auto it_src = lr_src.cbegin();
for (auto &kvp_dst : lr_dst) {
const auto &kvp_src = *it_src ++;
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
kvp_dst.second = kvp_src.second;
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
if (*lv < *rv)
return true;
else if (*lv > *rv)
return false;
}
return false;
}
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
if (*lv != *rv)
return false;
return true;
}
struct PrintObjectTrafoAndInstances
{
Transform3d trafo;
PrintInstances instances;
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
{
std::set<PrintObjectTrafoAndInstances> trafos;
PrintObjectTrafoAndInstances trafo;
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix();
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
// Reset the XY axes of the transformation.
trafo.trafo.data()[12] = 0;
trafo.trafo.data()[13] = 0;
// Search or insert a trafo.
auto it = trafos.emplace(trafo).first;
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
}
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
}
// Compare just the layer ranges and their layer heights, not the associated configs.
// Ignore the layer heights if check_layer_heights is false.
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
{
if (lr1.size() != lr2.size())
return false;
auto it2 = lr2.begin();
for (const auto &kvp1 : lr1) {
const auto &kvp2 = *it2 ++;
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
return false;
}
return true;
}
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
{
auto it_a = va.begin();
auto it_b = vb.begin();
while (it_a != va.end() || it_b != vb.end()) {
if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_a;
continue;
}
if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_b;
continue;
}
if (it_a == va.end() || it_b == vb.end())
// va or vb contains more Tool Changes than the other.
return true;
assert(it_a->type == CustomGCode::ToolChange);
assert(it_b->type == CustomGCode::ToolChange);
if (*it_a != *it_b)
// The two Tool Changes differ.
return true;
++ it_a;
++ it_b;
}
// There is no change in custom Tool Changes.
return false;
}
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
static t_config_option_keys print_config_diffs(
const PrintConfig &current_config,
const DynamicPrintConfig &new_full_config,
DynamicPrintConfig &filament_overrides)
{
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_";
t_config_option_keys print_diff;
for (const t_config_option_key &opt_key : current_config.keys()) {
const ConfigOption *opt_old = current_config.option(opt_key);
assert(opt_old != nullptr);
const ConfigOption *opt_new = new_full_config.option(opt_key);
// assert(opt_new != nullptr);
if (opt_new == nullptr)
//FIXME This may happen when executing some test cases.
continue;
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
auto opt_copy = opt_new->clone();
opt_copy->apply_override(opt_new_filament);
if (*opt_old == *opt_copy)
delete opt_copy;
else {
filament_overrides.set_key_value(opt_key, opt_copy);
print_diff.emplace_back(opt_key);
}
}
} else if (*opt_new != *opt_old)
print_diff.emplace_back(opt_key);
}
return print_diff;
}
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig &current_full_config, const DynamicPrintConfig &new_full_config)
{
t_config_option_keys full_config_diff;
for (const t_config_option_key &opt_key : new_full_config.keys()) {
const ConfigOption *opt_old = current_full_config.option(opt_key);
const ConfigOption *opt_new = new_full_config.option(opt_key);
if (opt_old == nullptr || *opt_new != *opt_old)
full_config_diff.emplace_back(opt_key);
}
return full_config_diff;
}
Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
// Normalize the config.
new_full_config.option("print_settings_id", true);
new_full_config.option("filament_settings_id", true);
new_full_config.option("printer_settings_id", true);
new_full_config.option("physical_printer_settings_id", true);
new_full_config.normalize_fdm();
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides;
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides);
t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config);
// Collect changes to object and region configs.
t_config_option_keys object_diff = m_default_object_config.diff(new_full_config);
t_config_option_keys region_diff = m_default_region_config.diff(new_full_config);
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated)
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
update_apply_status(false);
// Grab the lock for the Print / PrintObject milestones.
tbb::mutex::scoped_lock lock(this->state_mutex());
// The following call may stop the background processing.
if (! print_diff.empty())
update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty.
size_t num_extruders = m_config.nozzle_diameter.size();
bool num_extruders_changed = false;
if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
// see "Placeholders do not respect filament overrides." GH issue #3649
m_placeholder_parser.apply_config(filament_overrides);
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, print_diff, true);
//FIXME use move semantics once ConfigBase supports it.
m_config.apply(filament_overrides);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, object_diff, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
}
}
class LayerRanges
{
public:
LayerRanges() {}
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
void assign(const t_layer_config_ranges &in) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig *cfg = &range.second.get();
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR
// assert(it != m_ranges.end());
// assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
// assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
if (it == m_ranges.end() ||
std::abs(it->first.first - range.first) > EPSILON ||
std::abs(it->first.second - range.second) > EPSILON )
return nullptr; // desired range doesn't found
return (it == m_ranges.end()) ? nullptr : it->second;
}
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
private:
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
};
struct ModelObjectStatus {
enum Status {
Unknown,
Old,
New,
Moved,
Deleted,
};
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
std::set<ModelObjectStatus> model_object_status;
// 1) Synchronize model objects.
if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch.
// Stop background processing.
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
for (PrintObject *object : m_objects) {
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
update_apply_status(object->invalidate_all_steps());
delete object;
}
m_objects.clear();
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport));
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
m_model.objects.back()->set_model(&m_model);
}
} else {
// Reorder the objects, add new objects.
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
this->call_cancel_callback();
update_apply_status(this->invalidate_step(psGCodeExport));
// Second create a new list of objects.
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
m_model.objects.clear();
m_model.objects.reserve(model.objects.size());
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
for (const ModelObject *mobj : model.objects) {
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
// New ModelObject added.
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
m_model.objects.back()->set_model(&m_model);
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
} else {
// Existing ModelObject re-added (possibly moved in the list).
m_model.objects.emplace_back(*it);
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
}
}
bool deleted_any = false;
for (ModelObject *&model_object : model_objects_old) {
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
deleted_any = true;
} else
// Do not delete this ModelObject instance.
model_object = nullptr;
}
if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects.
PrintObjectPtrs print_objects_old = std::move(m_objects);
m_objects.clear();
m_objects.reserve(print_objects_old.size());
for (PrintObject *print_object : print_objects_old) {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
if (it_status->status == ModelObjectStatus::Deleted) {
update_apply_status(print_object->invalidate_all_steps());
delete print_object;
} else
m_objects.emplace_back(print_object);
}
for (ModelObject *model_object : model_objects_old)
delete model_object;
}
}
}
// 2) Map print objects including their transformation matrices.
struct PrintObjectStatus {
enum Status {
Unknown,
Deleted,
Reused,
New
};
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
id(print_object->model_object()->id()),
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
Transform3d trafo;
Status status;
// Search by id.
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
};
std::multiset<PrintObjectStatus> print_object_status;
for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object));
// 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object];
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object];
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
update_apply_status(it->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
if (supports_differ) {
this->call_cancel_callback();
update_apply_status(false);
}
// Invalidate just the supports step.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it)
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
if (supports_differ) {
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
}
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
update_apply_status(this->invalidate_step(psGCodeExport));
}
if (! model_parts_differ && ! modifiers_differ) {
// Synchronize Object's config.
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
if (object_config_changed)
model_object.config.assign_config(model_object_new.config);
if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
t_config_option_keys diff = it->print_object->config().diff(new_config);
if (! diff.empty()) {
update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff));
it->print_object->config_apply_only(new_config, diff, true);
}
}
}
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
model_object.invalidate_bounding_box();
// Synchronize the content of instances.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
(*old_instance)->printable = (*new_instance)->printable;
}
}
}
}
// 4) Generate PrintObjects from ModelObjects and their instances.
bool print_regions_reshuffled = false;
{
PrintObjectPtrs print_objects_new;
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
bool new_objects = false;
// Walk over all new model objects and check, whether there are matching PrintObjects.
for (ModelObject *model_object : m_model.objects) {
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
std::vector<const PrintObjectStatus*> old;
if (range.first != range.second) {
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
for (auto it = range.first; it != range.second; ++ it)
if (it->status != PrintObjectStatus::Deleted)
old.emplace_back(&(*it));
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
// Producing the config for PrintObject on demand, caching it at print_object_last.
const PrintObject *print_object_last = nullptr;
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
print_object->config_apply(print_object_last ?
print_object_last->config() :
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
print_object_last = print_object;
};
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) {
// Simple case, just generate new instances.
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
}
continue;
}
// Complex case, try to merge the two lists.
// Sort the old lexicographically by their trafos.
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists.
auto it_old = old.begin();
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
if (it_old != old.end())
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
} else {
// The PrintObject already exists and the copies differ.
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
print_objects_new.emplace_back((*it_old)->print_object);
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
}
}
}
if (m_objects != print_objects_new) {
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
deleted_objects = true;
}
if (new_objects || deleted_objects)
update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
if (new_objects)
update_apply_status(false);
print_regions_reshuffled = true;
}
print_object_status.clear();
}
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
for (PrintObject *print_object : m_objects) {
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
bool some_object_region_modified = false;
bool regions_merged = false;
for (size_t region_id = 0; region_id < print_object->m_region_volumes.size(); ++ region_id) {
PrintRegion &region = *print_object->m_all_regions[region_id];
PrintRegionConfig region_config;
bool region_config_set = false;
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : print_object->m_region_volumes[region_id].volumes) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_w_zrange.volume_idx];
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_w_zrange.layer_height_range);
PrintRegionConfig this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
if (region_config_set) {
if (this_region_config != region_config) {
regions_merged = true;
break;
}
} else {
region_config = std::move(this_region_config);
region_config_set = true;
}
}
if (regions_merged)
break;
size_t region_config_hash = region_config.hash();
bool modified = region.config_hash() != region_config_hash || region.config() != region_config;
some_object_region_modified |= modified;
if (some_object_region_modified)
// Verify whether this region was not merged with some other region.
for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *print_object->m_all_regions[i];
if (region_other.config_hash() == region_config_hash && region_other.config() == region_config) {
// Regions were merged. Reset this print_object.
regions_merged = true;
break;
}
}
if (modified) {
// Stop the background process before assigning new configuration to the regions.
t_config_option_keys diff = region.config().diff(region_config);
update_apply_status(print_object->invalidate_state_by_config_options(region.config(), region_config, diff));
region.config_apply_only(region_config, diff, false);
}
}
if (regions_merged) {
// Two regions of a single object were either split or merged. This invalidates the whole slicing.
update_apply_status(print_object->invalidate_all_steps());
print_object->m_region_volumes.clear();
}
}
// Possibly add new regions for the newly added or resetted PrintObjects.
for (size_t idx_print_object = 0; idx_print_object < m_objects.size();) {
PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_object();
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
if (print_object0.m_region_volumes.empty()) {
// Fresh or completely invalidated print_object. Assign regions.
unsigned int volume_id = 0;
for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id;
continue;
}
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
// Now insert a volume with a layer range to its own region.
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
int region_id = -1;
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
size_t hash = config.hash();
for (size_t i = 0; i < print_object0.m_all_regions.size(); ++ i)
if (hash == print_object0.m_all_regions[i]->config_hash() && config == *print_object0.m_all_regions[i]) {
region_id = int(i);
break;
}
// If no region exists with the same config, create a new one.
if (region_id == -1) {
region_id = int(print_object0.m_all_regions.size());
print_object0.m_all_regions.emplace_back(std::make_unique<PrintRegion>(std::move(config), hash));
}
print_object0.add_region_volume(region_id, volume_id, it_range->first);
}
++ volume_id;
}
print_regions_reshuffled = true;
}
for (++ idx_print_object; idx_print_object < m_objects.size() && m_objects[idx_print_object]->model_object() == &model_object; ++ idx_print_object) {
PrintObject &print_object = *m_objects[idx_print_object];
if (print_object.m_region_volumes.empty()) {
// Copy region volumes and regions from print_object0.
print_object.m_region_volumes = print_object0.m_region_volumes;
print_object.m_all_regions.reserve(print_object0.m_all_regions.size());
for (const std::unique_ptr<Slic3r::PrintRegion> &region : print_object0.m_all_regions)
print_object.m_all_regions.emplace_back(std::make_unique<PrintRegion>(*region));
print_regions_reshuffled = true;
}
}
}
if (print_regions_reshuffled) {
// Update Print::m_print_regions from objects.
struct cmp { bool operator() (const PrintRegion *l, const PrintRegion *r) const { return l->config_hash() == r->config_hash() && l->config() == r->config(); } };
std::set<const PrintRegion*, cmp> region_set;
m_print_regions.clear();
for (PrintObject *print_object : m_objects)
for (std::unique_ptr<Slic3r::PrintRegion> &print_region : print_object->m_all_regions)
if (auto it = region_set.find(print_region.get()); it == region_set.end()) {
int print_region_id = int(m_print_regions.size());
m_print_regions.emplace_back(print_region.get());
print_region->m_print_region_id = print_region_id;
} else {
print_region->m_print_region_id = (*it)->print_region_id();
}
}
// 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 */
return static_cast<ApplyStatus>(apply_status);
}
} // namespace Slic3r

View file

@ -97,6 +97,15 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
return status;
}
std::vector<std::reference_wrapper<const PrintRegion>> PrintObject::all_regions() const
{
std::vector<std::reference_wrapper<const PrintRegion>> out;
out.reserve(m_all_regions.size());
for (size_t i = 0; i < m_all_regions.size(); ++ i)
out.emplace_back(*m_all_regions[i]);
return out;
}
// Called by make_perimeters()
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
@ -173,8 +182,8 @@ void PrintObject::make_perimeters()
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = this->printing_region(region_id);
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
@ -294,7 +303,7 @@ void PrintObject::prepare_infill()
// Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
@ -313,7 +322,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
@ -332,7 +341,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
@ -351,7 +360,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
@ -716,10 +725,10 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false;
m_slicing_params.valid = false;
} else if (step == posSupportMaterial) {
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false;
m_slicing_params.valid = false;
}
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
@ -736,19 +745,11 @@ bool PrintObject::invalidate_all_steps()
// First call the "invalidate" functions, which may cancel background processing.
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
// Then reset some of the depending values.
this->m_slicing_params.valid = false;
this->region_volumes.clear();
m_slicing_params.valid = false;
m_region_volumes.clear();
return result;
}
static const PrintRegion* first_printing_region(const PrintObject &print_object)
{
for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region)
if (!print_object.region_volumes.empty())
return print_object.print()->regions()[idx_region];
return nullptr;
}
// This function analyzes slices of a region (SurfaceCollection slices).
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Initially all slices are of type stInternal.
@ -769,13 +770,13 @@ void PrintObject::detect_surfaces_type()
// should be visible.
bool spiral_vase = this->print()->config().spiral_vase.value;
bool interface_shells = ! spiral_vase && m_config.interface_shells.value;
size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size();
size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start";
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (Layer *layer : m_layers)
layer->m_regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
layer->m_regions[region_id]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads.
@ -791,7 +792,7 @@ void PrintObject::detect_surfaces_type()
((num_layers > 1) ? num_layers - 1 : num_layers) :
// In non-spiral vase mode, go over all layers.
m_layers.size()),
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
[this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print.
SurfaceType surface_type_bottom_other =
@ -799,9 +800,9 @@ void PrintObject::detect_surfaces_type()
stBottom : stBottomBridge;
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z;
Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region];
LayerRegion *layerm = layer->m_regions[region_id];
// comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
@ -814,7 +815,7 @@ void PrintObject::detect_surfaces_type()
Surfaces top;
if (upper_layer) {
ExPolygons upper_slices = interface_shells ?
diff_ex(layerm->slices.surfaces, upper_layer->m_regions[idx_region]->slices.surfaces, ApplySafetyOffset::Yes) :
diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) :
diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes);
surfaces_append(top, offset2_ex(upper_slices, -offset, offset), stTop);
} else {
@ -831,7 +832,7 @@ void PrintObject::detect_surfaces_type()
#if 0
//FIXME Why is this branch failing t\multi.t ?
Polygons lower_slices = interface_shells ?
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces) :
to_polygons(lower_layer->get_region(region_id)->slices.surfaces) :
to_polygons(lower_layer->slices);
surfaces_append(bottom,
offset2_ex(diff(layerm->slices.surfaces, lower_slices, true), -offset, offset),
@ -854,7 +855,7 @@ void PrintObject::detect_surfaces_type()
offset2_ex(
diff_ex(
intersection(layerm->slices.surfaces, lower_layer->lslices), // supported
lower_layer->m_regions[idx_region]->slices.surfaces,
lower_layer->m_regions[region_id]->slices.surfaces,
ApplySafetyOffset::Yes),
-offset, offset),
stBottom);
@ -887,7 +888,7 @@ void PrintObject::detect_surfaces_type()
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black")));
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, idx_region, layer->print_z).c_str(), expolygons_with_attributes);
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes);
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -924,25 +925,25 @@ void PrintObject::detect_surfaces_type()
if (interface_shells) {
// Move surfaces_new to layerm->slices.surfaces
for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer)
m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]);
m_layers[idx_layer]->m_regions[region_id]->slices.surfaces = std::move(surfaces_new[idx_layer]);
}
if (spiral_vase) {
if (num_layers > 1)
// Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
m_layers[num_layers - 1]->m_regions[idx_region]->slices.set_type(stTop);
m_layers[num_layers - 1]->m_regions[region_id]->slices.set_type(stTop);
for (size_t i = num_layers; i < m_layers.size(); ++ i)
m_layers[i]->m_regions[idx_region]->slices.set_type(stInternal);
m_layers[i]->m_regions[region_id]->slices.set_type(stInternal);
}
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start";
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start";
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, idx_region](const tbb::blocked_range<size_t>& range) {
[this, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region];
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
layerm->slices_to_fill_surfaces_clipped();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
@ -950,7 +951,7 @@ void PrintObject::detect_surfaces_type()
} // for each layer of a region
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end";
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end";
} // for each this->print->region_count
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
@ -966,8 +967,8 @@ void PrintObject::process_external_surfaces()
// Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
// over voids, which are supported by the layer below.
bool has_voids = false;
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id)
if (this->printing_region(region_id).config().fill_density == 0) {
has_voids = true;
break;
}
@ -1003,12 +1004,12 @@ void PrintObject::process_external_surfaces()
m_print->throw_if_canceled();
Polygons voids;
for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) {
if (layerm->region()->config().fill_density.value == 0.)
if (layerm->region().config().fill_density.value == 0.)
for (const Surface &surface : layerm->fill_surfaces.surfaces)
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
polygons_append(voids, offset(surface.expolygon, unsupported_width));
}
surfaces_covered[layer_idx] = diff(this->m_layers[layer_idx]->lslices, voids);
surfaces_covered[layer_idx] = diff(m_layers[layer_idx]->lslices, voids);
}
}
);
@ -1016,7 +1017,7 @@ void PrintObject::process_external_surfaces()
BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end";
}
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
@ -1024,7 +1025,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((int)region_id)->process_external_surfaces(
m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(
(layer_idx == 0) ? nullptr : m_layers[layer_idx - 1],
(layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]);
}
@ -1049,7 +1050,7 @@ void PrintObject::discover_vertical_shells()
Polygons holes;
};
bool spiral_vase = this->print()->config().spiral_vase.value;
size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size();
size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
// Does this region possibly produce more than 1 top or bottom layer?
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
@ -1064,14 +1065,14 @@ void PrintObject::discover_vertical_shells()
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
};
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value;
if (top_bottom_surfaces_all_regions) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false;
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) {
const PrintRegionConfig &config = m_print->get_region(idx_region)->config();
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
const PrintRegionConfig &config = this->printing_region(region_id).config();
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
has_extra_layers = true;
break;
@ -1087,7 +1088,7 @@ void PrintObject::discover_vertical_shells()
tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
const size_t num_regions = this->region_volumes.size();
const size_t num_regions = this->num_printing_regions();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
const Layer &layer = *m_layers[idx_layer];
@ -1099,8 +1100,8 @@ void PrintObject::discover_vertical_shells()
static size_t debug_idx = 0;
++ debug_idx;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
for (size_t idx_region = 0; idx_region < num_regions; ++ idx_region) {
LayerRegion &layerm = *layer.m_regions[idx_region];
for (size_t region_id = 0; region_id < num_regions; ++ region_id) {
LayerRegion &layerm = *layer.m_regions[region_id];
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
// Top surfaces.
append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing));
@ -1113,7 +1114,7 @@ void PrintObject::discover_vertical_shells()
unsigned int perimeters = 0;
for (Surface &s : layerm.slices.surfaces)
perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters);
perimeters += layerm.region()->config().perimeters.value;
perimeters += layerm.region().config().perimeters.value;
// Then calculate the infill offset.
if (perimeters > 0) {
Flow extflow = layerm.flow(frExternalPerimeter);
@ -1148,10 +1149,10 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
}
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
PROFILE_BLOCK(discover_vertical_shells_region);
const PrintRegion &region = *m_print->get_region(idx_region);
const PrintRegion &region = this->printing_region(region_id);
if (! region.config().ensure_vertical_shell_thickness.value)
// This region will be handled by discover_horizontal_shells().
continue;
@ -1165,15 +1166,15 @@ void PrintObject::discover_vertical_shells()
if (! top_bottom_surfaces_all_regions) {
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
// is calculated over a single material.
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom";
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : cache top / bottom";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, idx_region, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
[this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
Layer &layer = *m_layers[idx_layer];
LayerRegion &layerm = *layer.m_regions[idx_region];
LayerRegion &layerm = *layer.m_regions[region_id];
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
// Top surfaces.
auto &cache = cache_top_botom_regions[idx_layer];
@ -1182,21 +1183,21 @@ void PrintObject::discover_vertical_shells()
// Bottom surfaces.
cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing);
append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
// Holes over all regions. Only collect them once, they are valid for all idx_region iterations.
// Holes over all regions. Only collect them once, they are valid for all region_id iterations.
if (cache.holes.empty()) {
for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region)
polygons_append(cache.holes, to_polygons(layer.regions()[idx_region]->fill_expolygons));
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id)
polygons_append(cache.holes, to_polygons(layer.regions()[region_id]->fill_expolygons));
}
}
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom";
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end : cache top / bottom";
}
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness";
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, idx_region, &cache_top_botom_regions]
[this, region_id, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) {
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1208,8 +1209,8 @@ void PrintObject::discover_vertical_shells()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region];
const PrintRegionConfig &region_config = layerm->region()->config();
LayerRegion *layerm = layer->m_regions[region_id];
const PrintRegionConfig &region_config = layerm->region().config();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
@ -1423,11 +1424,11 @@ void PrintObject::discover_vertical_shells()
} // for each layer
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end";
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) {
LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region);
LayerRegion *layerm = m_layers[idx_layer]->get_region(region_id);
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final");
}
@ -1445,8 +1446,8 @@ void PrintObject::bridge_over_infill()
{
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = this->printing_region(region_id);
// skip bridging in case there are no voids
if (region.config().fill_density.value == 100)
@ -1627,9 +1628,15 @@ PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegion
void PrintObject::update_slicing_parameters()
{
#if ENABLE_ALLOW_NEGATIVE_Z
if (!m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders());
#else
if (! m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, unscale<double>(this->height()), this->object_extruders());
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
@ -1662,6 +1669,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
object_extruders);
}
sort_remove_duplicates(object_extruders);
//FIXME add painting extruders
if (object_max_z <= 0.f)
object_max_z = (float)model_object.raw_bounding_box().size().z();
@ -1672,10 +1680,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
std::vector<unsigned int> PrintObject::object_extruders() const
{
std::vector<unsigned int> extruders;
extruders.reserve(this->region_volumes.size() * 3);
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region)
if (! this->region_volumes[idx_region].empty())
m_print->get_region(idx_region)->collect_object_printing_extruders(extruders);
extruders.reserve(this->all_regions().size() * 3);
for (const PrintRegion &region : this->all_regions())
region.collect_object_printing_extruders(*this->print(), extruders);
sort_remove_duplicates(extruders);
return extruders;
}
@ -1691,6 +1698,15 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
updated = true;
}
#if ENABLE_ALLOW_NEGATIVE_Z
// Verify the layer_height_profile.
if (!layer_height_profile.empty() &&
// Must not be of even length.
((layer_height_profile.size() & 1) != 0 ||
// Last entry must be at the top of the object.
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max) > 1e-3))
layer_height_profile.clear();
#else
// Verify the layer_height_profile.
if (! layer_height_profile.empty() &&
// Must not be of even length.
@ -1698,6 +1714,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
// Last entry must be at the top of the object.
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3))
layer_height_profile.clear();
#endif // ENABLE_ALLOW_NEGATIVE_Z
if (layer_height_profile.empty()) {
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
@ -1743,8 +1760,8 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
layer->lower_layer = prev;
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
layer->add_region(this->print()->get_region(region_id));
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id)
layer->add_region(&this->printing_region(region_id));
prev = layer;
}
}
@ -1754,16 +1771,15 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
bool has_z_ranges = false;
size_t num_volumes = 0;
size_t num_modifiers = 0;
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
int last_volume_id = -1;
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
const int volume_id = volume_and_range.second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *model_volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (model_volume->is_model_part()) {
if (last_volume_id == volume_id) {
if (last_volume_id == volume_w_zrange.volume_idx) {
has_z_ranges = true;
} else {
last_volume_id = volume_id;
last_volume_id = volume_w_zrange.volume_idx;
if (all_volumes_single_region == -2)
// first model volume met
all_volumes_single_region = region_id;
@ -1786,14 +1802,14 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_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) {
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel
size_t slicing_mode_normal_below_layer = 0;
if (spiral_vase) {
// Slice the bottom layers with SlicingMode::Regular.
// This needs to be in sync with LayerRegion::make_perimeters() spiral_vase!
const PrintRegionConfig &config = this->print()->regions()[region_id]->config();
const PrintRegionConfig &config = this->printing_region(region_id).config();
slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value);
for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON;
++ slicing_mode_normal_below_layer);
@ -1819,22 +1835,22 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
};
std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
int volume_id = volumes_and_ranges[i].second;
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
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;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first);
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
ranges.back().second = volumes_and_ranges[j].first.second;
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
else
ranges.emplace_back(volumes_and_ranges[j].first);
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume));
i = j;
@ -1871,7 +1887,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}
}
// Collect and union volumes of a single region.
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
ExPolygons expolygons;
size_t num_volumes = 0;
for (SlicedVolume &sliced_volume : sliced_volumes)
@ -1892,8 +1908,8 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}
// Slice all modifier volumes.
if (this->region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
if (m_region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < m_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_modifiers(region_id, slice_zs);
@ -1906,7 +1922,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
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) {
for (size_t other_region_id = 0; other_region_id < m_region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
Layer *layer = m_layers[layer_id];
@ -2046,9 +2062,9 @@ end:
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const
{
std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
if (region_id < m_region_volumes.size()) {
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (volume->is_model_part())
volumes.emplace_back(volume);
}
@ -2056,27 +2072,27 @@ std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::v
return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes);
}
// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_and_range at most once.
// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_w_zrange at most once.
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
{
std::vector<ExPolygons> out;
if (region_id < this->region_volumes.size())
if (region_id < m_region_volumes.size())
{
std::vector<std::vector<t_layer_height_range>> volume_ranges;
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
volume_ranges.reserve(volumes_and_ranges.size());
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
int volume_id = volumes_and_ranges[i].second;
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
volume_ranges.reserve(volumes_and_ranges.volumes.size());
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) {
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first);
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) {
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
ranges.back().second = volumes_and_ranges[j].first.second;
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) {
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
else
ranges.emplace_back(volumes_and_ranges[j].first);
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
}
volume_ranges.emplace_back(std::move(ranges));
i = j;
@ -2098,8 +2114,8 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
// No modifier in this region was split to layer spans.
std::vector<const ModelVolume*> volumes;
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (volume->is_modifier())
volumes.emplace_back(volume);
}
@ -2107,19 +2123,19 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
} else {
// Some modifier in this region was split to layer spans.
std::vector<char> merge;
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
int volume_id = volumes_and_ranges[i].second;
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first);
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
ranges.emplace_back(volumes_and_ranges[j].first);
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume);
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
@ -2395,9 +2411,15 @@ void PrintObject::simplify_slices(double distance)
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
void PrintObject::clip_fill_surfaces()
{
if (! m_config.infill_only_where_needed.value ||
! std::any_of(this->print()->regions().begin(), this->print()->regions().end(),
[](const PrintRegion *region) { return region->config().fill_density > 0; }))
if (! m_config.infill_only_where_needed.value)
return;
bool has_infill = false;
for (size_t i = 0; i < this->num_printing_regions(); ++ i)
if (this->printing_region(i).config().fill_density > 0) {
has_infill = true;
break;
}
if (! has_infill)
return;
// We only want infill under ceilings; this is almost like an
@ -2450,7 +2472,7 @@ void PrintObject::clip_fill_surfaces()
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
// Apply new internal infill to regions.
for (LayerRegion *layerm : lower_layer->m_regions) {
if (layerm->region()->config().fill_density.value == 0)
if (layerm->region().config().fill_density.value == 0)
continue;
SurfaceType internal_surface_types[] = { stInternal, stInternalVoid };
Polygons internal;
@ -2475,12 +2497,12 @@ void PrintObject::discover_horizontal_shells()
{
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (size_t i = 0; i < m_layers.size(); ++ i) {
m_print->throw_if_canceled();
Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region()->config();
const PrintRegionConfig &region_config = layerm->region().config();
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
@ -2656,7 +2678,7 @@ void PrintObject::discover_horizontal_shells()
} // for each region
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
const LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
@ -2672,16 +2694,16 @@ void PrintObject::discover_horizontal_shells()
void PrintObject::combine_infill()
{
// Work on each region separately.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions()[region_id];
const size_t every = region->config().infill_every_layers.value;
if (every < 2 || region->config().fill_density == 0.)
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = this->printing_region(region_id);
const size_t every = region.config().infill_every_layers.value;
if (every < 2 || region.config().fill_density == 0.)
continue;
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height
double nozzle_diameter = std::min(
this->print()->config().nozzle_diameter.get_at(region->config().infill_extruder.value - 1),
this->print()->config().nozzle_diameter.get_at(region->config().solid_infill_extruder.value - 1));
this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1),
this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1));
// define the combinations
std::vector<size_t> combine(m_layers.size(), 0);
{
@ -2745,11 +2767,11 @@ void PrintObject::combine_infill()
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
// Because fill areas for rectilinear and honeycomb are grown
// later to overlap perimeters, we need to counteract that too.
((region->config().fill_pattern == ipRectilinear ||
region->config().fill_pattern == ipMonotonic ||
region->config().fill_pattern == ipGrid ||
region->config().fill_pattern == ipLine ||
region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
((region.config().fill_pattern == ipRectilinear ||
region.config().fill_pattern == ipMonotonic ||
region.config().fill_pattern == ipGrid ||
region.config().fill_pattern == ipLine ||
region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
layerms.back()->flow(frSolidInfill).scaled_width();
for (ExPolygon &expoly : intersection)
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));

View file

@ -20,11 +20,12 @@ unsigned int PrintRegion::extruder(FlowRole role) const
Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const
{
ConfigOptionFloatOrPercent config_width;
const PrintConfig &print_config = object.print()->config();
ConfigOptionFloatOrPercent config_width;
// Get extrusion width from configuration.
// (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) {
config_width = m_print->config().first_layer_extrusion_width;
if (first_layer && print_config.first_layer_extrusion_width.value > 0) {
config_width = print_config.first_layer_extrusion_width;
} else if (role == frExternalPerimeter) {
config_width = m_config.external_perimeter_extrusion_width;
} else if (role == frPerimeter) {
@ -44,7 +45,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1));
auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1));
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
}
@ -76,17 +77,17 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con
emplace_extruder(region_config.solid_infill_extruder);
}
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
void PrintRegion::collect_object_printing_extruders(const Print &print, std::vector<unsigned int> &object_extruders) const
{
// PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
// If not, then there must be something wrong with the Print::apply() function.
#ifndef NDEBUG
auto num_extruders = (int)print()->config().nozzle_diameter.size();
auto num_extruders = int(print.config().nozzle_diameter.size());
assert(this->config().perimeter_extruder <= num_extruders);
assert(this->config().infill_extruder <= num_extruders);
assert(this->config().solid_infill_extruder <= num_extruders);
#endif
collect_object_printing_extruders(print()->config(), this->config(), print()->has_brim(), object_extruders);
collect_object_printing_extruders(print.config(), this->config(), print.has_brim(), object_extruders);
}
}

View file

@ -267,7 +267,7 @@ protected:
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
{ m_config.apply_only(other, keys, ignore_nonexistent); }
void set_trafo(const Transform3d& trafo, bool left_handed) {
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });

View file

@ -345,17 +345,14 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
size_t num_nonempty_regions = 0;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id)
if (! object->region_volumes[region_id].empty()) {
++ num_nonempty_regions;
const PrintRegion &region = *object->print()->get_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow_ratio;
}
for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) {
const PrintRegion &region = object->printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow_ratio;
}
m_support_params.gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= num_nonempty_regions;
bridge_flow_ratio /= object->num_printing_regions();
m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ?
m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
@ -364,7 +361,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
m_support_params.can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value;
if (!m_support_params.can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = m_object->print()->object_extruders();
auto object_extruders = m_object->object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value))
// Object is printed with the same extruder as the support.
@ -1254,7 +1251,7 @@ namespace SupportMaterialInternal {
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
Polygons lower_grown_slices = offset(lower_layer_polygons,
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))),
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region().config().perimeter_extruder-1))),
SUPPORT_SURFACES_OFFSET_PARAMETERS);
// Collect perimeters of this layer.
//FIXME split_at_first_point() could split a bridge mid-way
@ -1640,7 +1637,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) {
coordf_t bridging_height = 0.;
for (const LayerRegion* region : layer.regions())
bridging_height += region->region()->bridging_height_avg(print_config);
bridging_height += region->region().bridging_height_avg(print_config);
bridging_height /= coordf_t(layer.regions().size());
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) {
@ -2767,13 +2764,13 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers()[i];
bool some_region_overlaps = false;
for (LayerRegion *region : object_layer.regions()) {
coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
coordf_t bridging_height = region->region().bridging_height_avg(*m_print_config);
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
some_region_overlaps = true;
polygons_append(polygons_trimming,
offset(region->fill_surfaces.filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (region->region()->config().overhangs.value)
if (region->region().config().overhangs.value)
// Add bridging perimeters.
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
}
@ -3185,7 +3182,7 @@ struct MyLayerExtruded
MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
this->layer = rhs.layer;
this->extrusions = std::move(rhs.extrusions);
this->m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude);
m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude);
rhs.layer = nullptr;
return *this;
}

View file

@ -41,15 +41,9 @@
//====================
#define ENABLE_2_4_0_ALPHA0 1
// Enable splitting of vertex buffers used to render toolpaths
#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_4_0_ALPHA0)
// Enable rendering only starting and final caps for toolpaths
#define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
// Enable reload from disk command for 3mf files
#define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0)
// Removes obsolete warning texture code
#define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_4_0_ALPHA0)
// Enable showing gcode line numbers in previeww horizontal slider
// Enable showing gcode line numbers in preview horizontal slider
#define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_4_0_ALPHA0)
// Enable validation of custom gcode against gcode processor reserved keywords
#define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_4_0_ALPHA0)
@ -59,10 +53,19 @@
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
// Enable a modified version of automatic downscale on load of objects too big
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
// Enable scrollable legend in preview
#define ENABLE_SCROLLABLE_LEGEND (1 && ENABLE_2_4_0_ALPHA0)
// Enable visualization of start gcode as regular toolpaths
#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
// Enable visualization of seams in preview
#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
// Enable project dirty state manager
#define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0)
// Enable project dirty state manager debug window
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE)
// Enable to push object instances under the bed
#define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0)
#define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z)
// Enable visualization of objects clearance for sequential prints
#define ENABLE_SEQUENTIAL_LIMITS (1 && ENABLE_2_4_0_ALPHA0)