Merge remote-tracking branch 'remotes/origin/master' into vb_print_regions

This commit is contained in:
Vojtech Bubnik 2021-05-19 09:40:24 +02:00
commit 682c405fc2
79 changed files with 4454 additions and 4526 deletions

View file

@ -199,6 +199,8 @@ add_library(libslic3r STATIC
Tesselate.hpp
TriangleMesh.cpp
TriangleMesh.hpp
TriangleMeshSlicer.cpp
TriangleMeshSlicer.hpp
TriangulateWall.hpp
TriangulateWall.cpp
utils.cpp

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());
@ -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.) {

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

@ -3,6 +3,7 @@
#include "ModelArrange.hpp"
#include "Geometry.hpp"
#include "MTUtils.hpp"
#include "TriangleMeshSlicer.hpp"
#include "TriangleSelector.hpp"
#include "Format/AMF.hpp"
@ -893,6 +894,30 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
Points pts;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
#if ENABLE_ALLOW_NEGATIVE_Z
const Transform3d trafo = trafo_instance * v->get_matrix();
const TriangleMesh& hull_3d = v->get_convex_hull();
const indexed_triangle_set& its = hull_3d.its;
if (its.vertices.empty()) {
// Using the STL faces.
const stl_file& stl = hull_3d.stl;
for (const stl_facet& facet : stl.facet_start) {
for (size_t j = 0; j < 3; ++j) {
const Vec3d p = trafo * facet.vertex[j].cast<double>();
if (p.z() >= 0.0)
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
}
}
}
else {
// Using the shared vertices should be a bit quicker than using the STL faces.
for (size_t i = 0; i < its.vertices.size(); ++i) {
const Vec3d p = trafo * its.vertices[i].cast<double>();
if (p.z() >= 0.0)
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
}
}
#else
Transform3d trafo = trafo_instance * v->get_matrix();
const indexed_triangle_set &its = v->mesh().its;
if (its.vertices.empty()) {
@ -901,15 +926,16 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
for (const stl_facet &facet : stl.facet_start)
for (size_t j = 0; j < 3; ++ j) {
Vec3d p = trafo * facet.vertex[j].cast<double>();
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
}
} else {
// Using the shared vertices should be a bit quicker than using the STL faces.
for (size_t i = 0; i < its.vertices.size(); ++ i) {
Vec3d p = trafo * its.vertices[i].cast<double>();
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
}
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
@ -942,30 +968,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 +1008,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 +1018,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 +1026,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()));
}
@ -1187,32 +1213,32 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
}
else if (! volume->mesh().empty()) {
TriangleMesh upper_mesh, lower_mesh;
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh();
mesh.require_shared_vertices();
// Perform cut
TriangleMeshSlicer tms(&mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh);
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
if (keep_upper) {
upper_mesh.repair();
upper_mesh.reset_repair_stats();
}
if (keep_lower) {
lower_mesh.repair();
lower_mesh.reset_repair_stats();
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
mesh.require_shared_vertices();
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
if (keep_upper) {
upper_mesh = TriangleMesh(upper_its);
upper_mesh.repair();
upper_mesh.reset_repair_stats();
}
if (keep_lower) {
lower_mesh = TriangleMesh(lower_its);
lower_mesh.repair();
lower_mesh.reset_repair_stats();
}
}
if (keep_upper && upper_mesh.facets_count() > 0) {
@ -1418,11 +1444,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 +1457,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)
@ -1906,12 +1929,19 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance =
#if ENABLE_ALLOW_NEGATIVE_Z
Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation,
get_scaling_factor(), get_mirror());
#else
Geometry::assemble_transform(Vec3d::Zero(), rotation,
get_scaling_factor(), get_mirror());
#endif // ENABLE_ALLOW_NEGATIVE_Z
Polygon p = get_object()->convex_hull_2d(trafo_instance);
#if !ENABLE_ALLOW_NEGATIVE_Z
assert(!p.points.empty());
#endif // !ENABLE_ALLOW_NEGATIVE_Z
// if (!p.points.empty()) {
// Polygons pp{p};

View file

@ -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)); }

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()) {
@ -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;

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

@ -371,6 +371,15 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
#if ENABLE_ALLOW_NEGATIVE_Z
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
offset(print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
jtRound, float(scale_(0.1))).front());
#else
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
offset(print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
@ -378,7 +387,8 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
jtRound, float(scale_(0.1))).front());
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull->second;
double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());

View file

@ -8,6 +8,7 @@
#include "Flow.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "TriangleMeshSlicer.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
#include "GCode/ThumbnailData.hpp"
@ -24,7 +25,6 @@ class Print;
class PrintObject;
class ModelObject;
class GCode;
enum class SlicingMode : uint32_t;
class Layer;
class SupportLayer;
@ -437,7 +437,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;

View file

@ -921,6 +921,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
ModelObjectStatusDB model_object_status_db;
// 1) Synchronize model objects.
bool print_regions_reshuffled = false;
if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch.
// Stop background processing.
@ -932,6 +933,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
delete object;
}
m_objects.clear();
print_regions_reshuffled = true;
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status_db.add(*model_object, ModelObjectStatus::New);
@ -1008,6 +1010,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
for (ModelObject *model_object : model_objects_old)
delete model_object;
print_regions_reshuffled = true;
}
}
}
@ -1124,7 +1127,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
// 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()));

View file

@ -10,6 +10,7 @@
#include "Surface.hpp"
#include "Slicing.hpp"
#include "Tesselate.hpp"
#include "TriangleMeshSlicer.hpp"
#include "Utils.hpp"
#include "Fill/FillAdaptive.hpp"
#include "Format/STL.hpp"
@ -1628,9 +1629,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)
@ -1692,6 +1699,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.
@ -1699,6 +1715,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);
@ -1745,7 +1762,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id)
layer->add_region(&this->print()->get_print_region(region_id));
layer->add_region(&this->printing_region(region_id));
prev = layer;
}
}
@ -1782,7 +1799,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
bool clipped = false;
bool upscaled = false;
bool spiral_vase = this->print()->config().spiral_vase;
auto slicing_mode = spiral_vase ? SlicingMode::PositiveLargestContour : SlicingMode::Regular;
auto slicing_mode = spiral_vase ? MeshSlicingParams::SlicingMode::PositiveLargestContour : MeshSlicingParams::SlicingMode::Regular;
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.
@ -1793,12 +1810,12 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
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()->get_print_region(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);
}
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, SlicingMode::Regular);
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode::Regular);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@ -2043,7 +2060,7 @@ end:
}
// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
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<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below) const
{
std::vector<const ModelVolume*> volumes;
if (region_id < m_region_volumes.size()) {
@ -2103,7 +2120,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
if (volume->is_modifier())
volumes.emplace_back(volume);
}
out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes);
out = this->slice_volumes(slice_zs, MeshSlicingParams::SlicingMode::Regular, volumes);
} else {
// Some modifier in this region was split to layer spans.
std::vector<char> merge;
@ -2121,7 +2138,7 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
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);
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, MeshSlicingParams::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.
if (out.empty()) {
out = std::move(this_slices);
@ -2162,7 +2179,7 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z);
return this->slice_volumes(zs, SlicingMode::Regular, volumes);
return this->slice_volumes(zs, MeshSlicingParams::SlicingMode::Regular, volumes);
}
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
@ -2177,7 +2194,7 @@ static void fix_mesh_connectivity(TriangleMesh &mesh)
std::vector<ExPolygons> PrintObject::slice_volumes(
const std::vector<float> &z,
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below,
const std::vector<const ModelVolume*> &volumes) const
{
std::vector<ExPolygons> layers;
@ -2202,19 +2219,17 @@ std::vector<ExPolygons> PrintObject::slice_volumes(
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
// TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
mesh.require_shared_vertices();
TriangleMeshSlicer mslicer;
mslicer.init(&mesh, callback);
mslicer.slice(z, mode, slicing_mode_normal_below_layer, mode_below, float(m_config.slice_closing_radius.value), &layers, callback);
MeshSlicingParamsEx params { { mode, slicing_mode_normal_below_layer, mode_below }, float(m_config.slice_closing_radius.value) };
layers = slice_mesh_ex(mesh.its, z, params, [print]() { print->throw_if_canceled(); });
m_print->throw_if_canceled();
}
}
return layers;
}
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
if (! z.empty()) {
@ -2229,13 +2244,13 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, S
// apply XY shift
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
// TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
mslicer.init(&mesh, callback);
mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback);
MeshSlicingParamsEx params;
params.mode = mode;
params.closing_radius = float(m_config.slice_closing_radius.value);
layers = slice_mesh_ex(mesh.its, z, params, [print](){ print->throw_if_canceled(); });
m_print->throw_if_canceled();
}
}
@ -2243,7 +2258,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, S
}
// Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping.
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const
{
std::vector<ExPolygons> out;
if (! z.empty() && ! ranges.empty()) {

View file

@ -2,6 +2,7 @@
#include <libslic3r/OpenVDBUtils.hpp>
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/TriangleMeshSlicer.hpp>
#include <libslic3r/SLA/Hollowing.hpp>
#include <libslic3r/SLA/IndexedMesh.hpp>
#include <libslic3r/ClipperUtils.hpp>
@ -295,11 +296,8 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
if (mesh.empty()) return;
mesh.require_shared_vertices();
TriangleMeshSlicer slicer(&mesh);
std::vector<ExPolygons> hole_slices;
slicer.slice(slicegrid, SlicingMode::Regular, closing_radius, &hole_slices, thr);
std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
if (obj_slices.size() != hole_slices.size())
BOOST_LOG_TRIVIAL(warning)

View file

@ -2,6 +2,7 @@
#include <libslic3r/SLA/SpatIndex.hpp>
#include <libslic3r/SLA/BoostAdapter.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/TriangleMeshSlicer.hpp>
#include "ConcaveHull.hpp"
@ -476,10 +477,9 @@ void pad_blueprint(const TriangleMesh & mesh,
ThrowOnCancel thrfn)
{
if (mesh.empty()) return;
TriangleMeshSlicer slicer(&mesh);
auto out = reserve_vector<ExPolygons>(heights.size());
slicer.slice(heights, SlicingMode::Regular, 0.f, &out, thrfn);
assert(mesh.has_shared_vertices());
std::vector<ExPolygons> out = slice_mesh_ex(mesh.its, heights, thrfn);
size_t count = 0;
for(auto& o : out) count += o.size();

View file

@ -72,7 +72,8 @@ public:
size_t width_px = 0;
size_t height_px = 0;
Resolution(size_t w = 0, size_t h = 0) : width_px(w), height_px(h) {}
Resolution() = default;
Resolution(size_t w, size_t h) : width_px(w), height_px(h) {}
size_t pixels() const { return width_px * height_px; }
};
@ -81,7 +82,8 @@ public:
double w_mm = 1.;
double h_mm = 1.;
PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0)
PixelDim() = default;
PixelDim(double px_width_mm, double px_height_mm)
: w_mm(px_width_mm), h_mm(px_height_mm)
{}
};

View file

@ -12,6 +12,7 @@
#include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Model.hpp>
#include <libslic3r/TriangleMeshSlicer.hpp>
#include <libnest2d/optimizers/nlopt/genetic.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp>
@ -44,9 +45,8 @@ std::vector<ExPolygons> SupportTree::slice(
if (!sup_mesh.empty()) {
slices.emplace_back();
TriangleMeshSlicer sup_slicer(&sup_mesh);
sup_slicer.slice(grid, SlicingMode::Regular, cr, &slices.back(), ctl().cancelfn);
assert(sup_mesh.has_shared_vertices());
slices.back() = slice_mesh_ex(sup_mesh.its, grid, cr, ctl().cancelfn);
}
if (!pad_mesh.empty()) {
@ -59,8 +59,8 @@ std::vector<ExPolygons> SupportTree::slice(
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
TriangleMeshSlicer pad_slicer(&pad_mesh);
pad_slicer.slice(padgrid, SlicingMode::Regular, cr, &slices.back(), ctl().cancelfn);
assert(pad_mesh.has_shared_vertices());
slices.back() = slice_mesh_ex(pad_mesh.its, padgrid, cr, ctl().cancelfn);
}
size_t len = grid.size();

View file

@ -3,6 +3,7 @@
#include <libslic3r/Exception.hpp>
#include <libslic3r/SLAPrintSteps.hpp>
#include <libslic3r/MeshBoolean.hpp>
#include <libslic3r/TriangleMeshSlicer.hpp>
// Need the cylinder method for the the drainholes in hollowing step
#include <libslic3r/SLA/SupportTreeBuilder.hpp>
@ -198,7 +199,7 @@ static std::vector<bool> create_exclude_mask(
std::vector<bool> exclude_mask(its.indices.size(), false);
std::vector< std::vector<size_t> > neighbor_index =
create_neighbor_index(its);
create_vertex_faces_index(its);
auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face)
{
@ -470,13 +471,12 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
for(auto it = slindex_it; it != po.m_slice_index.end(); ++it)
po.m_model_height_levels.emplace_back(it->slice_level());
TriangleMeshSlicer slicer(&mesh);
po.m_model_slices.clear();
float closing_r = float(po.config().slice_closing_radius.value);
auto thr = [this]() { m_print->throw_if_canceled(); };
auto &slice_grid = po.m_model_height_levels;
slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr);
assert(mesh.has_shared_vertices());
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, closing_r, thr);
sla::Interior *interior = po.m_hollowing_data ?
po.m_hollowing_data->interior.get() :
@ -486,9 +486,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
TriangleMesh interiormesh = sla::get_mesh(*interior);
interiormesh.repaired = false;
interiormesh.repair(true);
TriangleMeshSlicer interior_slicer(&interiormesh);
std::vector<ExPolygons> interior_slices;
interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr);
std::vector<ExPolygons> interior_slices = slice_mesh_ex(interiormesh.its, slice_grid, closing_r, thr);
sla::ccr::for_each(size_t(0), interior_slices.size(),
[&po, &interior_slices] (size_t i) {

View file

@ -83,12 +83,6 @@ void SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width)
this->draw(l, stroke, stroke_width);
}
void SVG::draw(const IntersectionLines &lines, std::string stroke)
{
for (const IntersectionLine &il : lines)
this->draw((Line)il, stroke);
}
void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity)
{
this->fill = fill;

View file

@ -43,8 +43,7 @@ public:
void draw(const Line &line, std::string stroke = "black", coordf_t stroke_width = 0);
void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width = 0);
void draw(const Lines &lines, std::string stroke = "black", coordf_t stroke_width = 0);
void draw(const IntersectionLines &lines, std::string stroke = "black");
void draw(const ExPolygon &expolygon, std::string fill = "grey", const float fill_opacity=1.f);
void draw_outline(const ExPolygon &polygon, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0);
void draw(const ExPolygons &expolygons, std::string fill = "grey", const float fill_opacity=1.f);

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)
#endif // _prusaslicer_technologies_h_

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@
#include <admesh/stl.h>
#include <functional>
#include <vector>
#include <boost/thread.hpp>
#include "BoundingBox.hpp"
#include "Line.hpp"
#include "Point.hpp"
@ -24,7 +23,7 @@ public:
TriangleMesh() : repaired(false) {}
TriangleMesh(const Pointf3s &points, const std::vector<Vec3i> &facets);
explicit TriangleMesh(const indexed_triangle_set &M);
void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
@ -47,7 +46,7 @@ public:
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3d& t, bool fix_left_handed = false);
void transform(const Matrix3d& t, bool fix_left_handed = false);
void transform(const Matrix3d& t, bool fix_left_handed = false);
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;
@ -62,7 +61,7 @@ public:
// Return the size of the mesh in coordinates.
Vec3d size() const { return stl.stats.size.cast<double>(); }
/// Return the center of the related bounding box.
Vec3d center() const { return this->bounding_box().center(); }
Vec3d center() const { return this->bounding_box().center(); }
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
// Slice this mesh at the provided Z levels and return the vector
@ -78,8 +77,8 @@ public:
size_t memsize() const;
// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
size_t release_optional();
// Restore optional data possibly released by release_optional().
void restore_optional();
// Restore optional data possibly released by release_optional().
void restore_optional();
stl_file stl;
indexed_triangle_set its;
@ -92,160 +91,27 @@ private:
// Create an index of faces belonging to each vertex. The returned vector can
// be indexed with vertex indices and contains a list of face indices for each
// vertex.
std::vector< std::vector<size_t> >
create_neighbor_index(const indexed_triangle_set &its);
std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its);
enum FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges.
feGeneral,
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
feTop,
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
feBottom,
// All three vertices of a face are aligned with the cutting plane.
feHorizontal
};
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
// Two neighbor faces share a unique edge identifier even if they are flipped.
// Used for chaining slice lines into polygons.
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its);
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
class IntersectionReference
{
public:
IntersectionReference() : point_id(-1), edge_id(-1) {}
IntersectionReference(int point_id, int edge_id) : point_id(point_id), edge_id(edge_id) {}
// Where is this intersection point located? On mesh vertex or mesh edge?
// Only one of the following will be set, the other will remain set to -1.
// Index of the mesh vertex.
int point_id;
// Index of the mesh edge.
int edge_id;
};
// Merge duplicate vertices, return number of vertices removed.
// This function will happily create non-manifolds if more than two faces share the same vertex position
// or more than two faces share the same edge position!
int its_merge_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
class IntersectionPoint : public Point, public IntersectionReference
{
public:
IntersectionPoint() {}
IntersectionPoint(int point_id, int edge_id, const Point &pt) : IntersectionReference(point_id, edge_id), Point(pt) {}
IntersectionPoint(const IntersectionReference &ir, const Point &pt) : IntersectionReference(ir), Point(pt) {}
// Inherits coord_t x, y
};
// Remove degenerate faces, return number of faces removed.
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = true);
class IntersectionLine : public Line
{
public:
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
// Remove vertices, which none of the faces references. Return number of freed vertices.
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
bool skip() const { return (this->flags & SKIP) != 0; }
void set_skip() { this->flags |= SKIP; }
bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
// Inherits Point a, b
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
// Vertex indices of the line end points.
int a_id;
int b_id;
// Source mesh edges of the line end points.
int edge_a_id;
int edge_b_id;
// feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type;
// Used by TriangleMeshSlicer::slice() to skip duplicate edges.
enum {
// Triangle edge added, because it has no neighbor.
EDGE0_NO_NEIGHBOR = 0x001,
EDGE1_NO_NEIGHBOR = 0x002,
EDGE2_NO_NEIGHBOR = 0x004,
// Triangle edge added, because it makes a fold with another horizontal edge.
EDGE0_FOLD = 0x010,
EDGE1_FOLD = 0x020,
EDGE2_FOLD = 0x040,
// The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
NO_SEED = 0x100,
SKIP = 0x200,
};
uint32_t flags;
};
typedef std::vector<IntersectionLine> IntersectionLines;
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
enum class SlicingMode : uint32_t {
// Regular slicing, maintain all contours and their orientation.
Regular,
// Maintain all contours, orient all contours CCW, therefore all holes are being closed.
Positive,
// Orient all contours CCW and keep only the contour with the largest area.
// This mode is useful for slicing complex objects in vase mode.
PositiveLargestContour,
};
class TriangleMeshSlicer
{
public:
typedef std::function<void()> throw_on_cancel_callback_type;
TriangleMeshSlicer() : mesh(nullptr) {}
TriangleMeshSlicer(const TriangleMesh* mesh) { this->init(mesh, [](){}); }
void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(
const std::vector<float> &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode,
std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, SlicingMode mode, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{ return this->slice(z, mode, 0, mode, layers, throw_on_cancel); }
void slice(
const std::vector<float> &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, const float closing_radius,
std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, SlicingMode mode, const float closing_radius,
std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{ this->slice(z, mode, 0, mode, closing_radius, layers, throw_on_cancel); }
enum FacetSliceType {
NoSlice = 0,
Slicing = 1,
Cutting = 2
};
FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, IntersectionLine *line_out) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
void set_up_direction(const Vec3f& up);
private:
const TriangleMesh *mesh;
// Map from a facet to an edge index.
std::vector<int> facets_edges;
// Scaled copy of this->mesh->stl.v_shared
std::vector<stl_vertex> v_scaled_shared;
// Quaternion that will be used to rotate every facet before the slicing
Eigen::Quaternion<float, Eigen::DontAlign> m_quaternion;
// Whether or not the above quaterion should be used
bool m_use_quaternion = false;
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
void make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const;
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const;
};
inline void slice_mesh(
const TriangleMesh & mesh,
const std::vector<float> & z,
std::vector<Polygons> & layers,
TriangleMeshSlicer::throw_on_cancel_callback_type thr = nullptr)
{
if (mesh.empty()) return;
TriangleMeshSlicer slicer(&mesh);
slicer.slice(z, SlicingMode::Regular, &layers, thr);
}
inline void slice_mesh(
const TriangleMesh & mesh,
const std::vector<float> & z,
std::vector<ExPolygons> & layers,
float closing_radius,
TriangleMeshSlicer::throw_on_cancel_callback_type thr = nullptr)
{
if (mesh.empty()) return;
TriangleMeshSlicer slicer(&mesh);
slicer.slice(z, SlicingMode::Regular, closing_radius, &layers, thr);
}
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
void its_shrink_to_fit(indexed_triangle_set &its);
TriangleMesh make_cube(double x, double y, double z);
@ -259,21 +125,21 @@ TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
// Serialization through the Cereal library
#include <cereal/access.hpp>
namespace cereal {
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
stl_allocate(&stl);
archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
stl_get_size(&stl);
mesh.repair();
}
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
const stl_file& stl = mesh.stl;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
}
}
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
const stl_file& stl = mesh.stl;
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,83 @@
#ifndef slic3r_TriangleMeshSlicer_hpp_
#define slic3r_TriangleMeshSlicer_hpp_
#include <functional>
#include <vector>
#include "Polygon.hpp"
#include "ExPolygon.hpp"
namespace Slic3r {
struct MeshSlicingParams
{
enum class SlicingMode : uint32_t {
// Regular slicing, maintain all contours and their orientation.
Regular,
// Maintain all contours, orient all contours CCW, therefore all holes are being closed.
Positive,
// Orient all contours CCW and keep only the contour with the largest area.
// This mode is useful for slicing complex objects in vase mode.
PositiveLargestContour,
};
SlicingMode mode { SlicingMode::Regular };
// For vase mode: below this layer a different slicing mode will be used to produce a single contour.
// 0 = ignore.
size_t slicing_mode_normal_below_layer { 0 };
// Mode to apply below slicing_mode_normal_below_layer. Ignored if slicing_mode_nromal_below_layer == 0.
SlicingMode mode_below { SlicingMode::Regular };
// Transforming faces during the slicing.
Transform3d trafo { Transform3d::Identity() };
};
struct MeshSlicingParamsEx : public MeshSlicingParams
{
// Morphological closing operation when creating output expolygons.
float closing_radius { 0 };
// Positive offset applied when creating output expolygons.
float extra_offset { 0 };
// Resolution for contour simplification, scaled!
// 0 = don't simplify.
double resolution { 0 };
};
std::vector<Polygons> slice_mesh(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
const MeshSlicingParams &params,
std::function<void()> throw_on_cancel = []{});
std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
const MeshSlicingParamsEx &params,
std::function<void()> throw_on_cancel = []{});
inline std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
std::function<void()> throw_on_cancel = []{})
{
return slice_mesh_ex(mesh, zs, MeshSlicingParamsEx{}, throw_on_cancel);
}
inline std::vector<ExPolygons> slice_mesh_ex(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,
float closing_radius,
std::function<void()> throw_on_cancel = []{})
{
MeshSlicingParamsEx params;
params.closing_radius = closing_radius;
return slice_mesh_ex(mesh, zs, params, throw_on_cancel);
}
void cut_mesh(
const indexed_triangle_set &mesh,
float z,
indexed_triangle_set *upper,
indexed_triangle_set *lower);
}
#endif // slic3r_TriangleMeshSlicer_hpp_