2018-11-08 20:18:40 +01:00
|
|
|
#include "SLAPrint.hpp"
|
2019-11-12 16:53:47 +01:00
|
|
|
#include "SLAPrintSteps.hpp"
|
|
|
|
|
2019-02-21 15:46:04 +01:00
|
|
|
#include "ClipperUtils.hpp"
|
2019-04-08 13:35:03 +02:00
|
|
|
#include "Geometry.hpp"
|
2018-11-22 13:42:52 +01:00
|
|
|
#include "MTUtils.hpp"
|
2020-10-22 13:53:08 +02:00
|
|
|
#include "Thread.hpp"
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2018-11-21 18:03:24 +01:00
|
|
|
#include <unordered_set>
|
2018-11-22 18:13:34 +01:00
|
|
|
#include <numeric>
|
2018-11-21 18:03:24 +01:00
|
|
|
|
2018-11-13 17:33:03 +01:00
|
|
|
#include <tbb/parallel_for.h>
|
2019-02-13 08:44:42 +01:00
|
|
|
#include <boost/filesystem/path.hpp>
|
2018-11-20 16:12:04 +01:00
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
|
2019-08-16 16:31:05 +02:00
|
|
|
// #define SLAPRINT_DO_BENCHMARK
|
2019-08-16 16:17:37 +02:00
|
|
|
|
|
|
|
#ifdef SLAPRINT_DO_BENCHMARK
|
|
|
|
#include <libnest2d/tools/benchmark.h>
|
|
|
|
#endif
|
|
|
|
|
2018-11-13 17:33:03 +01:00
|
|
|
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
|
|
|
|
2018-11-09 18:32:35 +01:00
|
|
|
#include "I18N.hpp"
|
|
|
|
|
|
|
|
//! macro used to mark string used at localization,
|
|
|
|
//! return same string
|
|
|
|
#define L(s) Slic3r::I18N::translate(s)
|
2018-11-08 20:18:40 +01:00
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
|
|
|
|
bool is_zero_elevation(const SLAPrintObjectConfig &c)
|
2019-06-14 18:10:16 +02:00
|
|
|
{
|
2019-11-12 16:53:47 +01:00
|
|
|
return c.pad_enable.getBool() && c.pad_around_object.getBool();
|
|
|
|
}
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
// Compile the argument for support creation from the static print config.
|
2020-06-25 13:58:51 +02:00
|
|
|
sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c)
|
2019-11-06 13:38:43 +01:00
|
|
|
{
|
2020-06-25 13:58:51 +02:00
|
|
|
sla::SupportTreeConfig scfg;
|
2019-11-06 13:38:43 +01:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
scfg.enabled = c.supports_enable.getBool();
|
|
|
|
scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat();
|
2020-07-08 11:31:01 +02:00
|
|
|
double pillar_r = 0.5 * c.support_pillar_diameter.getFloat();
|
|
|
|
scfg.head_back_radius_mm = pillar_r;
|
|
|
|
scfg.head_fallback_radius_mm =
|
|
|
|
0.01 * c.support_small_pillar_diameter_percent.getFloat() * pillar_r;
|
2019-11-12 16:53:47 +01:00
|
|
|
scfg.head_penetration_mm = c.support_head_penetration.getFloat();
|
|
|
|
scfg.head_width_mm = c.support_head_width.getFloat();
|
|
|
|
scfg.object_elevation_mm = is_zero_elevation(c) ?
|
|
|
|
0. : c.support_object_elevation.getFloat();
|
|
|
|
scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ;
|
|
|
|
scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat();
|
|
|
|
scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat();
|
|
|
|
switch(c.support_pillar_connection_mode.getInt()) {
|
|
|
|
case slapcmZigZag:
|
|
|
|
scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break;
|
|
|
|
case slapcmCross:
|
|
|
|
scfg.pillar_connection_mode = sla::PillarConnectionMode::cross; break;
|
|
|
|
case slapcmDynamic:
|
|
|
|
scfg.pillar_connection_mode = sla::PillarConnectionMode::dynamic; break;
|
|
|
|
}
|
|
|
|
scfg.ground_facing_only = c.support_buildplate_only.getBool();
|
|
|
|
scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat();
|
|
|
|
scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat();
|
|
|
|
scfg.base_height_mm = c.support_base_height.getFloat();
|
|
|
|
scfg.pillar_base_safety_distance_mm =
|
|
|
|
c.support_base_safety_distance.getFloat() < EPSILON ?
|
|
|
|
scfg.safety_distance_mm : c.support_base_safety_distance.getFloat();
|
|
|
|
|
2020-03-02 12:43:00 +01:00
|
|
|
scfg.max_bridges_on_pillar = unsigned(c.support_max_bridges_on_pillar.getInt());
|
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
return scfg;
|
|
|
|
}
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c)
|
2018-11-09 18:32:35 +01:00
|
|
|
{
|
2019-11-12 16:53:47 +01:00
|
|
|
sla::PadConfig::EmbedObject ret;
|
|
|
|
|
|
|
|
ret.enabled = is_zero_elevation(c);
|
|
|
|
|
|
|
|
if(ret.enabled) {
|
|
|
|
ret.everywhere = c.pad_around_object_everywhere.getBool();
|
|
|
|
ret.object_gap_mm = c.pad_object_gap.getFloat();
|
|
|
|
ret.stick_width_mm = c.pad_object_connector_width.getFloat();
|
|
|
|
ret.stick_stride_mm = c.pad_object_connector_stride.getFloat();
|
|
|
|
ret.stick_penetration_mm = c.pad_object_connector_penetration
|
|
|
|
.getFloat();
|
2019-05-09 14:15:56 +02:00
|
|
|
}
|
2019-11-12 16:53:47 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c)
|
2018-11-09 18:32:35 +01:00
|
|
|
{
|
2019-11-12 16:53:47 +01:00
|
|
|
sla::PadConfig pcfg;
|
|
|
|
|
|
|
|
pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat();
|
|
|
|
pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0;
|
|
|
|
|
|
|
|
pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat();
|
|
|
|
pcfg.wall_height_mm = c.pad_wall_height.getFloat();
|
|
|
|
pcfg.brim_size_mm = c.pad_brim_size.getFloat();
|
|
|
|
|
|
|
|
// set builtin pad implicitly ON
|
|
|
|
pcfg.embed_object = builtin_pad_cfg(c);
|
|
|
|
|
|
|
|
return pcfg;
|
|
|
|
}
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg)
|
2018-11-09 18:32:35 +01:00
|
|
|
{
|
2019-11-12 16:53:47 +01:00
|
|
|
// An empty pad can only be created if embed_object mode is enabled
|
|
|
|
// and the pad is not forced everywhere
|
|
|
|
return !pad.empty() || (pcfg.embed_object.enabled && !pcfg.embed_object.everywhere);
|
2018-11-09 18:32:35 +01:00
|
|
|
}
|
|
|
|
|
2018-11-08 20:18:40 +01:00
|
|
|
void SLAPrint::clear()
|
|
|
|
{
|
2018-11-27 13:52:42 +01:00
|
|
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
2018-11-09 12:02:42 +01:00
|
|
|
// The following call should stop background processing if it is running.
|
|
|
|
this->invalidate_all_steps();
|
2018-12-03 16:25:21 +01:00
|
|
|
for (SLAPrintObject *object : m_objects)
|
|
|
|
delete object;
|
2018-11-27 13:52:42 +01:00
|
|
|
m_objects.clear();
|
2018-12-03 16:25:21 +01:00
|
|
|
m_model.clear_objects();
|
2018-11-08 20:18:40 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
// Transformation without rotation around Z and without a shift by X and Y.
|
2020-01-29 10:07:10 +01:00
|
|
|
Transform3d SLAPrint::sla_trafo(const ModelObject &model_object) const
|
2018-11-21 17:35:35 +01:00
|
|
|
{
|
2019-04-03 17:34:46 +02:00
|
|
|
|
2020-01-29 10:07:10 +01:00
|
|
|
Vec3d corr = this->relative_correction();
|
2019-04-03 17:34:46 +02:00
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
ModelInstance &model_instance = *model_object.instances.front();
|
|
|
|
Vec3d offset = model_instance.get_offset();
|
|
|
|
Vec3d rotation = model_instance.get_rotation();
|
|
|
|
offset(0) = 0.;
|
|
|
|
offset(1) = 0.;
|
|
|
|
rotation(2) = 0.;
|
2019-04-03 18:23:29 +02:00
|
|
|
|
|
|
|
offset(Z) *= corr(Z);
|
|
|
|
|
|
|
|
auto trafo = Transform3d::Identity();
|
|
|
|
trafo.translate(offset);
|
|
|
|
trafo.scale(corr);
|
|
|
|
trafo.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
|
|
|
|
trafo.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY()));
|
|
|
|
trafo.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX()));
|
|
|
|
trafo.scale(model_instance.get_scaling_factor());
|
|
|
|
trafo.scale(model_instance.get_mirror());
|
|
|
|
|
2019-04-03 17:34:46 +02:00
|
|
|
if (model_instance.is_left_handed())
|
|
|
|
trafo = Eigen::Scaling(Vec3d(-1., 1., 1.)) * trafo;
|
|
|
|
|
2019-04-02 13:47:49 +02:00
|
|
|
return trafo;
|
2018-11-21 17:35:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// List of instances, where the ModelInstance transformation is a composite of sla_trafo and the transformation defined by SLAPrintObject::Instance.
|
|
|
|
static std::vector<SLAPrintObject::Instance> sla_instances(const ModelObject &model_object)
|
|
|
|
{
|
|
|
|
std::vector<SLAPrintObject::Instance> instances;
|
2019-04-08 13:35:03 +02:00
|
|
|
assert(! model_object.instances.empty());
|
|
|
|
if (! model_object.instances.empty()) {
|
|
|
|
Vec3d rotation0 = model_object.instances.front()->get_rotation();
|
|
|
|
rotation0(2) = 0.;
|
|
|
|
for (ModelInstance *model_instance : model_object.instances)
|
|
|
|
if (model_instance->is_printable()) {
|
|
|
|
instances.emplace_back(
|
|
|
|
model_instance->id(),
|
|
|
|
Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)),
|
2019-05-09 14:15:56 +02:00
|
|
|
float(Geometry::rotation_diff_z(rotation0, model_instance->get_rotation())));
|
2019-04-08 13:35:03 +02:00
|
|
|
}
|
|
|
|
}
|
2018-11-21 17:35:35 +01:00
|
|
|
return instances;
|
|
|
|
}
|
|
|
|
|
2020-10-14 16:48:56 +02:00
|
|
|
std::vector<ObjectID> SLAPrint::print_object_ids() const
|
|
|
|
{
|
|
|
|
std::vector<ObjectID> out;
|
|
|
|
// Reserve one more for the caller to append the ID of the Print itself.
|
|
|
|
out.reserve(m_objects.size() + 1);
|
|
|
|
for (const SLAPrintObject *print_object : m_objects)
|
|
|
|
out.emplace_back(print_object->id());
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-07-25 14:39:19 +02:00
|
|
|
SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config)
|
2018-11-08 20:18:40 +01:00
|
|
|
{
|
2018-11-21 17:35:35 +01:00
|
|
|
#ifdef _DEBUG
|
|
|
|
check_model_ids_validity(model);
|
|
|
|
#endif /* _DEBUG */
|
2018-11-09 12:02:42 +01:00
|
|
|
|
2019-07-25 14:39:19 +02:00
|
|
|
// Normalize the config.
|
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 10:48:44 +01:00
|
|
|
config.option("sla_print_settings_id", true);
|
|
|
|
config.option("sla_material_settings_id", true);
|
|
|
|
config.option("printer_settings_id", true);
|
|
|
|
config.option("physical_printer_settings_id", true);
|
2018-11-21 17:35:35 +01:00
|
|
|
// Collect changes to print config.
|
2019-04-03 18:23:29 +02:00
|
|
|
t_config_option_keys print_diff = m_print_config.diff(config);
|
2018-11-21 17:35:35 +01:00
|
|
|
t_config_option_keys printer_diff = m_printer_config.diff(config);
|
|
|
|
t_config_option_keys material_diff = m_material_config.diff(config);
|
|
|
|
t_config_option_keys object_diff = m_default_object_config.diff(config);
|
2019-07-25 17:08:31 +02:00
|
|
|
t_config_option_keys placeholder_parser_diff = m_placeholder_parser.config_diff(config);
|
2018-11-21 17:35:35 +01:00
|
|
|
|
2018-11-27 13:52:42 +01:00
|
|
|
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
|
2018-11-21 17:35:35 +01:00
|
|
|
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
|
|
|
|
auto update_apply_status = [&apply_status](bool invalidated)
|
|
|
|
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
|
2018-12-03 13:14:28 +01:00
|
|
|
if (! (print_diff.empty() && printer_diff.empty() && material_diff.empty() && object_diff.empty()))
|
2018-11-21 17:35:35 +01:00
|
|
|
update_apply_status(false);
|
2018-11-14 18:04:43 +01:00
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
// Grab the lock for the Print / PrintObject milestones.
|
2018-11-27 13:52:42 +01:00
|
|
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
2018-11-21 17:35:35 +01:00
|
|
|
|
|
|
|
// The following call may stop the background processing.
|
2019-04-05 11:56:11 +02:00
|
|
|
bool invalidate_all_model_objects = false;
|
2018-12-03 13:14:28 +01:00
|
|
|
if (! print_diff.empty())
|
2019-04-05 11:56:11 +02:00
|
|
|
update_apply_status(this->invalidate_state_by_config_options(print_diff, invalidate_all_model_objects));
|
2018-11-27 13:52:42 +01:00
|
|
|
if (! printer_diff.empty())
|
2019-04-05 11:56:11 +02:00
|
|
|
update_apply_status(this->invalidate_state_by_config_options(printer_diff, invalidate_all_model_objects));
|
2018-11-27 13:52:42 +01:00
|
|
|
if (! material_diff.empty())
|
2019-04-05 11:56:11 +02:00
|
|
|
update_apply_status(this->invalidate_state_by_config_options(material_diff, invalidate_all_model_objects));
|
2018-11-21 17:35:35 +01:00
|
|
|
|
2018-12-03 13:14:28 +01:00
|
|
|
// Apply variables to placeholder parser. The placeholder parser is currently used
|
|
|
|
// only to generate the output file name.
|
2019-04-03 18:23:29 +02:00
|
|
|
if (! placeholder_parser_diff.empty()) {
|
2018-12-03 13:14:28 +01:00
|
|
|
// update_apply_status(this->invalidate_step(slapsRasterize));
|
2019-07-25 17:08:31 +02:00
|
|
|
m_placeholder_parser.apply_config(config);
|
2018-12-03 13:14:28 +01:00
|
|
|
// Set the profile aliases for the PrintBase::output_filename()
|
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 10:48:44 +01:00
|
|
|
m_placeholder_parser.set("print_preset", config.option("sla_print_settings_id")->clone());
|
|
|
|
m_placeholder_parser.set("material_preset", config.option("sla_material_settings_id")->clone());
|
|
|
|
m_placeholder_parser.set("printer_preset", config.option("printer_settings_id")->clone());
|
|
|
|
m_placeholder_parser.set("physical_printer_preset", config.option("physical_printer_settings_id")->clone());
|
2018-12-03 13:14:28 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
|
2018-12-03 13:14:28 +01:00
|
|
|
m_print_config.apply_only(config, print_diff, true);
|
2018-11-27 13:52:42 +01:00
|
|
|
m_printer_config.apply_only(config, printer_diff, true);
|
2018-11-21 17:35:35 +01:00
|
|
|
// Handle changes to material config.
|
|
|
|
m_material_config.apply_only(config, material_diff, true);
|
|
|
|
// Handle changes to object config defaults
|
|
|
|
m_default_object_config.apply_only(config, object_diff, true);
|
2020-04-13 12:31:37 +02:00
|
|
|
|
|
|
|
if (m_printer) m_printer->apply(m_printer_config);
|
2018-11-21 17:35:35 +01:00
|
|
|
|
|
|
|
struct ModelObjectStatus {
|
|
|
|
enum Status {
|
|
|
|
Unknown,
|
|
|
|
Old,
|
|
|
|
New,
|
|
|
|
Moved,
|
|
|
|
Deleted,
|
|
|
|
};
|
2019-06-27 11:02:45 +02:00
|
|
|
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
|
|
|
|
ObjectID id;
|
2018-11-21 17:35:35 +01:00
|
|
|
Status status;
|
|
|
|
// Search by id.
|
|
|
|
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
|
|
|
};
|
|
|
|
std::set<ModelObjectStatus> model_object_status;
|
|
|
|
|
|
|
|
// 1) Synchronize model objects.
|
2019-04-05 11:56:11 +02:00
|
|
|
if (model.id() != m_model.id() || invalidate_all_model_objects) {
|
2018-11-21 17:35:35 +01:00
|
|
|
// Kill everything, initialize from scratch.
|
|
|
|
// Stop background processing.
|
2019-02-21 12:39:38 +01:00
|
|
|
this->call_cancel_callback();
|
2018-11-21 17:35:35 +01:00
|
|
|
update_apply_status(this->invalidate_all_steps());
|
|
|
|
for (SLAPrintObject *object : m_objects) {
|
|
|
|
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
2018-12-22 10:02:42 +01:00
|
|
|
update_apply_status(object->invalidate_all_steps());
|
2018-11-21 17:35:35 +01:00
|
|
|
delete object;
|
|
|
|
}
|
|
|
|
m_objects.clear();
|
2018-11-09 12:02:42 +01:00
|
|
|
m_model.assign_copy(model);
|
2018-11-21 17:35:35 +01:00
|
|
|
for (const ModelObject *model_object : m_model.objects)
|
|
|
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
|
|
|
|
} else {
|
|
|
|
if (model_object_list_equal(m_model, model)) {
|
|
|
|
// The object list did not change.
|
|
|
|
for (const ModelObject *model_object : m_model.objects)
|
|
|
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
|
|
|
} else if (model_object_list_extended(m_model, model)) {
|
|
|
|
// Add new objects. Their volumes and configs will be synchronized later.
|
2019-03-27 10:59:29 +01:00
|
|
|
update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
|
2018-11-21 17:35:35 +01:00
|
|
|
for (const ModelObject *model_object : m_model.objects)
|
|
|
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
|
|
|
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
|
|
|
|
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
|
|
|
|
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
|
|
|
|
m_model.objects.back()->set_model(&m_model);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Reorder the objects, add new objects.
|
|
|
|
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
2019-02-21 12:39:38 +01:00
|
|
|
this->call_cancel_callback();
|
2019-03-27 10:59:29 +01:00
|
|
|
update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
|
2018-11-21 17:35:35 +01:00
|
|
|
// Second create a new list of objects.
|
|
|
|
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
|
|
|
m_model.objects.clear();
|
|
|
|
m_model.objects.reserve(model.objects.size());
|
|
|
|
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
|
|
|
|
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
|
|
|
|
for (const ModelObject *mobj : model.objects) {
|
|
|
|
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
|
|
|
|
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
|
|
|
|
// New ModelObject added.
|
|
|
|
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
|
|
|
|
m_model.objects.back()->set_model(&m_model);
|
|
|
|
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
|
|
|
|
} else {
|
|
|
|
// Existing ModelObject re-added (possibly moved in the list).
|
|
|
|
m_model.objects.emplace_back(*it);
|
|
|
|
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool deleted_any = false;
|
|
|
|
for (ModelObject *&model_object : model_objects_old) {
|
|
|
|
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
|
|
|
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
|
|
|
|
deleted_any = true;
|
|
|
|
} else
|
|
|
|
// Do not delete this ModelObject instance.
|
|
|
|
model_object = nullptr;
|
|
|
|
}
|
|
|
|
if (deleted_any) {
|
|
|
|
// Delete PrintObjects of the deleted ModelObjects.
|
|
|
|
std::vector<SLAPrintObject*> print_objects_old = std::move(m_objects);
|
|
|
|
m_objects.clear();
|
|
|
|
m_objects.reserve(print_objects_old.size());
|
|
|
|
for (SLAPrintObject *print_object : print_objects_old) {
|
|
|
|
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
|
|
|
assert(it_status != model_object_status.end());
|
|
|
|
if (it_status->status == ModelObjectStatus::Deleted) {
|
|
|
|
update_apply_status(print_object->invalidate_all_steps());
|
|
|
|
delete print_object;
|
|
|
|
} else
|
|
|
|
m_objects.emplace_back(print_object);
|
|
|
|
}
|
|
|
|
for (ModelObject *model_object : model_objects_old)
|
|
|
|
delete model_object;
|
2018-11-13 17:33:03 +01:00
|
|
|
}
|
2018-11-09 12:02:42 +01:00
|
|
|
}
|
2018-11-12 11:46:38 +01:00
|
|
|
}
|
2018-11-09 12:02:42 +01:00
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
// 2) Map print objects including their transformation matrices.
|
|
|
|
struct PrintObjectStatus {
|
|
|
|
enum Status {
|
|
|
|
Unknown,
|
|
|
|
Deleted,
|
|
|
|
Reused,
|
|
|
|
New
|
|
|
|
};
|
2018-11-27 13:52:42 +01:00
|
|
|
PrintObjectStatus(SLAPrintObject *print_object, Status status = Unknown) :
|
2018-11-21 17:35:35 +01:00
|
|
|
id(print_object->model_object()->id()),
|
|
|
|
print_object(print_object),
|
|
|
|
trafo(print_object->trafo()),
|
|
|
|
status(status) {}
|
2019-06-27 11:02:45 +02:00
|
|
|
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
|
2018-11-21 17:35:35 +01:00
|
|
|
// ID of the ModelObject & PrintObject
|
2019-06-27 11:02:45 +02:00
|
|
|
ObjectID id;
|
2018-11-21 17:35:35 +01:00
|
|
|
// Pointer to the old PrintObject
|
|
|
|
SLAPrintObject *print_object;
|
2018-11-27 13:52:42 +01:00
|
|
|
// Trafo generated with model_object->world_matrix(true)
|
2018-11-21 17:35:35 +01:00
|
|
|
Transform3d trafo;
|
|
|
|
Status status;
|
|
|
|
// Search by id.
|
|
|
|
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
|
|
|
|
};
|
|
|
|
std::multiset<PrintObjectStatus> print_object_status;
|
|
|
|
for (SLAPrintObject *print_object : m_objects)
|
|
|
|
print_object_status.emplace(PrintObjectStatus(print_object));
|
|
|
|
|
|
|
|
// 3) Synchronize ModelObjects & PrintObjects.
|
|
|
|
std::vector<SLAPrintObject*> print_objects_new;
|
|
|
|
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
|
|
|
|
bool new_objects = false;
|
|
|
|
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
|
|
|
|
ModelObject &model_object = *m_model.objects[idx_model_object];
|
|
|
|
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
|
|
|
assert(it_status != model_object_status.end());
|
|
|
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
2019-04-03 18:23:29 +02:00
|
|
|
// PrintObject for this ModelObject, if it exists.
|
|
|
|
auto it_print_object_status = print_object_status.end();
|
|
|
|
if (it_status->status != ModelObjectStatus::New) {
|
|
|
|
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
|
|
|
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
|
|
|
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
|
|
|
it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
|
|
|
|
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
|
|
|
it_print_object_status = print_object_status.end();
|
|
|
|
// Check whether a model part volume was added or removed, their transformations or order changed.
|
|
|
|
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
|
|
|
bool sla_trafo_differs =
|
|
|
|
model_object.instances.empty() != model_object_new.instances.empty() ||
|
|
|
|
(! model_object.instances.empty() &&
|
2020-01-29 10:07:10 +01:00
|
|
|
(! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)) ||
|
2019-04-03 18:23:29 +02:00
|
|
|
model_object.instances.front()->is_left_handed() != model_object_new.instances.front()->is_left_handed()));
|
|
|
|
if (model_parts_differ || sla_trafo_differs) {
|
|
|
|
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
|
|
|
if (it_print_object_status != print_object_status.end()) {
|
|
|
|
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
|
|
|
|
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
|
|
|
}
|
|
|
|
// Copy content of the ModelObject including its ID, do not change the parent.
|
|
|
|
model_object.assign_copy(model_object_new);
|
|
|
|
} else {
|
|
|
|
// Synchronize Object's config.
|
2020-09-24 15:34:13 +02:00
|
|
|
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
|
2019-04-03 18:23:29 +02:00
|
|
|
if (object_config_changed)
|
2020-09-24 15:34:13 +02:00
|
|
|
model_object.config.assign_config(model_object_new.config);
|
2019-04-03 18:23:29 +02:00
|
|
|
if (! object_diff.empty() || object_config_changed) {
|
|
|
|
SLAPrintObjectConfig new_config = m_default_object_config;
|
2020-09-24 19:03:09 +02:00
|
|
|
new_config.apply(model_object.config.get(), true);
|
2019-04-03 18:23:29 +02:00
|
|
|
if (it_print_object_status != print_object_status.end()) {
|
|
|
|
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
|
|
|
|
if (! diff.empty()) {
|
|
|
|
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
|
|
|
|
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
|
|
|
|
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
|
|
|
|
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
|
|
|
|
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
|
|
|
|
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
|
|
|
|
if (it_print_object_status != print_object_status.end())
|
|
|
|
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
|
|
|
|
|
|
|
model_object.sla_support_points = model_object_new.sla_support_points;
|
|
|
|
}
|
2019-07-25 11:23:23 +02:00
|
|
|
model_object.sla_points_status = model_object_new.sla_points_status;
|
2019-11-13 15:55:37 +01:00
|
|
|
|
2019-11-15 15:48:52 +01:00
|
|
|
// Invalidate hollowing if drain holes have changed
|
|
|
|
if (model_object.sla_drain_holes != model_object_new.sla_drain_holes)
|
2019-11-13 15:55:37 +01:00
|
|
|
{
|
|
|
|
model_object.sla_drain_holes = model_object_new.sla_drain_holes;
|
2020-02-03 15:42:54 +01:00
|
|
|
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposDrillHoles));
|
2019-11-13 15:55:37 +01:00
|
|
|
}
|
2019-04-03 18:23:29 +02:00
|
|
|
|
|
|
|
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
|
|
|
model_object.name = model_object_new.name;
|
|
|
|
model_object.input_file = model_object_new.input_file;
|
|
|
|
model_object.clear_instances();
|
|
|
|
model_object.instances.reserve(model_object_new.instances.size());
|
|
|
|
for (const ModelInstance *model_instance : model_object_new.instances) {
|
|
|
|
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
|
|
|
model_object.instances.back()->set_model_object(&model_object);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-21 17:35:35 +01:00
|
|
|
|
|
|
|
std::vector<SLAPrintObject::Instance> new_instances = sla_instances(model_object);
|
|
|
|
if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) {
|
|
|
|
// The SLAPrintObject is already there.
|
2019-04-03 18:23:29 +02:00
|
|
|
if (new_instances.empty()) {
|
|
|
|
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
|
|
|
} else {
|
|
|
|
if (new_instances != it_print_object_status->print_object->instances()) {
|
|
|
|
// Instances changed.
|
|
|
|
it_print_object_status->print_object->set_instances(new_instances);
|
2019-03-27 10:59:29 +01:00
|
|
|
update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
|
2019-04-03 18:23:29 +02:00
|
|
|
}
|
|
|
|
print_objects_new.emplace_back(it_print_object_status->print_object);
|
|
|
|
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
|
|
|
}
|
|
|
|
} else if (! new_instances.empty()) {
|
2018-11-21 17:35:35 +01:00
|
|
|
auto print_object = new SLAPrintObject(this, &model_object);
|
2019-02-05 15:57:19 +01:00
|
|
|
|
|
|
|
// FIXME: this invalidates the transformed mesh in SLAPrintObject
|
|
|
|
// which is expensive to calculate (especially the raw_mesh() call)
|
2020-01-29 10:07:10 +01:00
|
|
|
print_object->set_trafo(sla_trafo(model_object), model_object.instances.front()->is_left_handed());
|
2019-02-05 15:57:19 +01:00
|
|
|
|
2019-04-02 17:48:50 +02:00
|
|
|
print_object->set_instances(std::move(new_instances));
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2020-09-24 19:03:09 +02:00
|
|
|
print_object->config_apply(m_default_object_config, true);
|
|
|
|
print_object->config_apply(model_object.config.get(), true);
|
2018-11-21 17:35:35 +01:00
|
|
|
print_objects_new.emplace_back(print_object);
|
|
|
|
new_objects = true;
|
|
|
|
}
|
2018-11-12 11:46:38 +01:00
|
|
|
}
|
2018-11-09 12:02:42 +01:00
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
if (m_objects != print_objects_new) {
|
2019-02-21 12:39:38 +01:00
|
|
|
this->call_cancel_callback();
|
2018-11-21 17:35:35 +01:00
|
|
|
update_apply_status(this->invalidate_all_steps());
|
|
|
|
m_objects = print_objects_new;
|
|
|
|
// Delete the PrintObjects marked as Unknown or Deleted.
|
|
|
|
for (auto &pos : print_object_status)
|
|
|
|
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
|
2018-12-22 10:02:42 +01:00
|
|
|
update_apply_status(pos.print_object->invalidate_all_steps());
|
2018-11-21 17:35:35 +01:00
|
|
|
delete pos.print_object;
|
|
|
|
}
|
2018-12-22 10:02:42 +01:00
|
|
|
if (new_objects)
|
|
|
|
update_apply_status(false);
|
2018-11-21 17:35:35 +01:00
|
|
|
}
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2019-04-25 18:48:31 +02:00
|
|
|
if(m_objects.empty()) {
|
2019-08-29 10:24:55 +02:00
|
|
|
m_printer_input = {};
|
|
|
|
m_print_statistics = {};
|
2019-04-25 18:48:31 +02:00
|
|
|
}
|
2018-11-21 17:35:35 +01:00
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
check_model_ids_equal(m_model, model);
|
|
|
|
#endif /* _DEBUG */
|
|
|
|
|
2019-07-25 14:39:19 +02:00
|
|
|
m_full_print_config = std::move(config);
|
2018-11-21 17:35:35 +01:00
|
|
|
return static_cast<ApplyStatus>(apply_status);
|
2018-11-08 20:18:40 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 11:40:56 +01:00
|
|
|
// After calling the apply() function, set_task() may be called to limit the task to be processed by process().
|
2019-02-21 08:44:07 +01:00
|
|
|
void SLAPrint::set_task(const TaskParams ¶ms)
|
|
|
|
{
|
2019-04-03 18:23:29 +02:00
|
|
|
// Grab the lock for the Print / PrintObject milestones.
|
|
|
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
2019-02-21 08:44:07 +01:00
|
|
|
|
2019-04-03 18:23:29 +02:00
|
|
|
int n_object_steps = int(params.to_object_step) + 1;
|
|
|
|
if (n_object_steps == 0)
|
2019-06-11 12:40:07 +02:00
|
|
|
n_object_steps = int(slaposCount);
|
2019-02-21 08:44:07 +01:00
|
|
|
|
2019-04-03 18:23:29 +02:00
|
|
|
if (params.single_model_object.valid()) {
|
2019-02-25 10:21:12 +01:00
|
|
|
// Find the print object to be processed with priority.
|
2019-04-03 18:23:29 +02:00
|
|
|
SLAPrintObject *print_object = nullptr;
|
|
|
|
size_t idx_print_object = 0;
|
|
|
|
for (; idx_print_object < m_objects.size(); ++ idx_print_object)
|
|
|
|
if (m_objects[idx_print_object]->model_object()->id() == params.single_model_object) {
|
|
|
|
print_object = m_objects[idx_print_object];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(print_object != nullptr);
|
2019-02-25 10:21:12 +01:00
|
|
|
// Find out whether the priority print object is being currently processed.
|
|
|
|
bool running = false;
|
2019-04-03 18:23:29 +02:00
|
|
|
for (int istep = 0; istep < n_object_steps; ++ istep) {
|
2019-06-11 12:40:07 +02:00
|
|
|
if (! print_object->m_stepmask[size_t(istep)])
|
2019-02-25 10:21:12 +01:00
|
|
|
// Step was skipped, cancel.
|
2019-04-03 18:23:29 +02:00
|
|
|
break;
|
|
|
|
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
|
2019-02-25 10:21:12 +01:00
|
|
|
// No step was skipped, and a wanted step is being processed. Don't cancel.
|
2019-04-03 18:23:29 +02:00
|
|
|
running = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! running)
|
|
|
|
this->call_cancel_callback();
|
|
|
|
|
|
|
|
// Now the background process is either stopped, or it is inside one of the print object steps to be calculated anyway.
|
|
|
|
if (params.single_model_instance_only) {
|
|
|
|
// Suppress all the steps of other instances.
|
|
|
|
for (SLAPrintObject *po : m_objects)
|
2019-06-11 12:40:07 +02:00
|
|
|
for (size_t istep = 0; istep < slaposCount; ++ istep)
|
2019-04-03 18:23:29 +02:00
|
|
|
po->m_stepmask[istep] = false;
|
|
|
|
} else if (! running) {
|
|
|
|
// Swap the print objects, so that the selected print_object is first in the row.
|
|
|
|
// At this point the background processing must be stopped, so it is safe to shuffle print objects.
|
|
|
|
if (idx_print_object != 0)
|
|
|
|
std::swap(m_objects.front(), m_objects[idx_print_object]);
|
|
|
|
}
|
2019-02-25 10:21:12 +01:00
|
|
|
// and set the steps for the current object.
|
2019-04-03 18:23:29 +02:00
|
|
|
for (int istep = 0; istep < n_object_steps; ++ istep)
|
2019-06-11 12:40:07 +02:00
|
|
|
print_object->m_stepmask[size_t(istep)] = true;
|
|
|
|
for (int istep = n_object_steps; istep < int(slaposCount); ++ istep)
|
|
|
|
print_object->m_stepmask[size_t(istep)] = false;
|
2019-04-03 18:23:29 +02:00
|
|
|
} else {
|
2019-02-25 10:21:12 +01:00
|
|
|
// Slicing all objects.
|
|
|
|
bool running = false;
|
|
|
|
for (SLAPrintObject *print_object : m_objects)
|
|
|
|
for (int istep = 0; istep < n_object_steps; ++ istep) {
|
2019-06-11 12:40:07 +02:00
|
|
|
if (! print_object->m_stepmask[size_t(istep)]) {
|
2019-02-25 10:21:12 +01:00
|
|
|
// Step may have been skipped. Restart.
|
|
|
|
goto loop_end;
|
|
|
|
}
|
|
|
|
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
|
|
|
|
// This step is running, and the state cannot be changed due to the this->state_mutex() being locked.
|
|
|
|
// It is safe to manipulate m_stepmask of other SLAPrintObjects and SLAPrint now.
|
|
|
|
running = true;
|
|
|
|
goto loop_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loop_end:
|
|
|
|
if (! running)
|
|
|
|
this->call_cancel_callback();
|
|
|
|
for (SLAPrintObject *po : m_objects) {
|
|
|
|
for (int istep = 0; istep < n_object_steps; ++ istep)
|
2019-06-11 12:40:07 +02:00
|
|
|
po->m_stepmask[size_t(istep)] = true;
|
|
|
|
for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep)
|
2019-02-25 10:21:12 +01:00
|
|
|
po->m_stepmask[istep] = false;
|
|
|
|
}
|
|
|
|
}
|
2019-02-21 11:40:56 +01:00
|
|
|
|
|
|
|
if (params.to_object_step != -1 || params.to_print_step != -1) {
|
|
|
|
// Limit the print steps.
|
2019-04-03 18:23:29 +02:00
|
|
|
size_t istep = (params.to_object_step != -1) ? 0 : size_t(params.to_print_step) + 1;
|
|
|
|
for (; istep < m_stepmask.size(); ++ istep)
|
|
|
|
m_stepmask[istep] = false;
|
2019-02-21 11:40:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up after process() finished, either with success, error or if canceled.
|
|
|
|
// The adjustments on the SLAPrint / SLAPrintObject data due to set_task() are to be reverted here.
|
|
|
|
void SLAPrint::finalize()
|
|
|
|
{
|
|
|
|
for (SLAPrintObject *po : m_objects)
|
2019-06-11 12:40:07 +02:00
|
|
|
for (size_t istep = 0; istep < slaposCount; ++ istep)
|
2019-04-03 18:23:29 +02:00
|
|
|
po->m_stepmask[istep] = true;
|
2019-06-11 12:40:07 +02:00
|
|
|
for (size_t istep = 0; istep < slapsCount; ++ istep)
|
2019-02-21 11:40:56 +01:00
|
|
|
m_stepmask[istep] = true;
|
2019-02-21 08:44:07 +01:00
|
|
|
}
|
|
|
|
|
2019-02-21 15:46:04 +01:00
|
|
|
// Generate a recommended output file name based on the format template, default extension, and template parameters
|
|
|
|
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
|
|
|
|
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before the output is finalized).
|
2019-05-17 16:27:00 +02:00
|
|
|
std::string SLAPrint::output_filename(const std::string &filename_base) const
|
2019-04-03 18:23:29 +02:00
|
|
|
{
|
2019-02-21 15:46:04 +01:00
|
|
|
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
2020-02-12 18:24:48 +01:00
|
|
|
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config);
|
2019-02-21 15:46:04 +01:00
|
|
|
}
|
|
|
|
|
2021-02-26 08:23:37 +01:00
|
|
|
std::string SLAPrint::validate(std::string*) const
|
2019-03-18 15:31:47 +01:00
|
|
|
{
|
|
|
|
for(SLAPrintObject * po : m_objects) {
|
2019-03-26 17:16:50 +01:00
|
|
|
|
|
|
|
const ModelObject *mo = po->model_object();
|
2019-04-17 10:08:54 +02:00
|
|
|
bool supports_en = po->config().supports_enable.getBool();
|
2019-03-26 17:16:50 +01:00
|
|
|
|
2019-04-17 10:08:54 +02:00
|
|
|
if(supports_en &&
|
2019-03-26 17:16:50 +01:00
|
|
|
mo->sla_points_status == sla::PointsStatus::UserModified &&
|
|
|
|
mo->sla_support_points.empty())
|
|
|
|
return L("Cannot proceed without support points! "
|
|
|
|
"Add support points or disable support generation.");
|
|
|
|
|
2020-06-25 13:58:51 +02:00
|
|
|
sla::SupportTreeConfig cfg = make_support_cfg(po->config());
|
2019-03-18 15:31:47 +01:00
|
|
|
|
2019-06-11 12:40:07 +02:00
|
|
|
double elv = cfg.object_elevation_mm;
|
2019-08-28 11:32:49 +02:00
|
|
|
|
2019-09-24 15:15:49 +02:00
|
|
|
sla::PadConfig padcfg = make_pad_cfg(po->config());
|
|
|
|
sla::PadConfig::EmbedObject &builtinpad = padcfg.embed_object;
|
|
|
|
|
|
|
|
if(supports_en && !builtinpad.enabled && elv < cfg.head_fullwidth())
|
2019-08-06 16:51:32 +02:00
|
|
|
return L(
|
|
|
|
"Elevation is too low for object. Use the \"Pad around "
|
2019-09-04 15:15:10 +02:00
|
|
|
"object\" feature to print the object without elevation.");
|
2019-08-28 11:32:49 +02:00
|
|
|
|
2019-06-12 13:15:42 +02:00
|
|
|
if(supports_en && builtinpad.enabled &&
|
|
|
|
cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
|
|
|
|
return L(
|
|
|
|
"The endings of the support pillars will be deployed on the "
|
|
|
|
"gap between the object and the pad. 'Support base safety "
|
|
|
|
"distance' has to be greater than the 'Pad object gap' "
|
|
|
|
"parameter to avoid this.");
|
|
|
|
}
|
2019-09-24 15:15:49 +02:00
|
|
|
|
|
|
|
std::string pval = padcfg.validate();
|
|
|
|
if (!pval.empty()) return pval;
|
2019-03-18 15:31:47 +01:00
|
|
|
}
|
2018-12-13 12:42:45 +01:00
|
|
|
|
2019-08-20 17:24:48 +02:00
|
|
|
double expt_max = m_printer_config.max_exposure_time.getFloat();
|
|
|
|
double expt_min = m_printer_config.min_exposure_time.getFloat();
|
2019-08-20 15:49:32 +02:00
|
|
|
double expt_cur = m_material_config.exposure_time.getFloat();
|
|
|
|
|
|
|
|
if (expt_cur < expt_min || expt_cur > expt_max)
|
2019-08-20 17:24:48 +02:00
|
|
|
return L("Exposition time is out of printer profile bounds.");
|
2019-08-20 15:49:32 +02:00
|
|
|
|
2019-08-20 17:24:48 +02:00
|
|
|
double iexpt_max = m_printer_config.max_initial_exposure_time.getFloat();
|
|
|
|
double iexpt_min = m_printer_config.min_initial_exposure_time.getFloat();
|
2019-08-20 15:49:32 +02:00
|
|
|
double iexpt_cur = m_material_config.initial_exposure_time.getFloat();
|
|
|
|
|
|
|
|
if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max)
|
2019-08-20 17:24:48 +02:00
|
|
|
return L("Initial exposition time is out of printer profile bounds.");
|
2019-08-20 15:49:32 +02:00
|
|
|
|
2019-03-18 15:31:47 +01:00
|
|
|
return "";
|
2018-11-23 13:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-04-13 12:31:37 +02:00
|
|
|
void SLAPrint::set_printer(SLAPrinter *arch)
|
|
|
|
{
|
|
|
|
invalidate_step(slapsRasterize);
|
|
|
|
m_printer = arch;
|
|
|
|
}
|
|
|
|
|
2019-03-27 10:59:29 +01:00
|
|
|
bool SLAPrint::invalidate_step(SLAPrintStep step)
|
|
|
|
{
|
|
|
|
bool invalidated = Inherited::invalidate_step(step);
|
|
|
|
|
|
|
|
// propagate to dependent steps
|
|
|
|
if (step == slapsMergeSlicesAndEval) {
|
|
|
|
invalidated |= this->invalidate_all_steps();
|
|
|
|
}
|
|
|
|
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
2018-11-08 20:18:40 +01:00
|
|
|
void SLAPrint::process()
|
|
|
|
{
|
2020-10-22 13:53:08 +02:00
|
|
|
if (m_objects.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
name_tbb_thread_pool_threads();
|
2019-03-21 18:01:41 +01:00
|
|
|
|
2018-11-09 18:32:35 +01:00
|
|
|
// Assumption: at this point the print objects should be populated only with
|
|
|
|
// the model objects we have to process and the instances are also filtered
|
2019-11-06 13:38:43 +01:00
|
|
|
|
2020-04-13 12:31:37 +02:00
|
|
|
Steps printsteps(this);
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
// We want to first process all objects...
|
|
|
|
std::vector<SLAPrintObjectStep> level1_obj_steps = {
|
2020-01-31 08:58:21 +01:00
|
|
|
slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad
|
2018-11-13 11:53:54 +01:00
|
|
|
};
|
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
// and then slice all supports to allow preview to be displayed ASAP
|
|
|
|
std::vector<SLAPrintObjectStep> level2_obj_steps = {
|
|
|
|
slaposSliceSupports
|
|
|
|
};
|
|
|
|
|
|
|
|
SLAPrintStep print_steps[] = { slapsMergeSlicesAndEval, slapsRasterize };
|
2019-11-12 16:53:47 +01:00
|
|
|
|
|
|
|
double st = Steps::min_objstatus;
|
2018-11-22 12:13:58 +01:00
|
|
|
|
2018-12-14 16:58:55 +01:00
|
|
|
BOOST_LOG_TRIVIAL(info) << "Start slicing process.";
|
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
#ifdef SLAPRINT_DO_BENCHMARK
|
|
|
|
Benchmark bench;
|
|
|
|
#else
|
|
|
|
struct {
|
|
|
|
void start() {} void stop() {} double getElapsedSec() { return .0; }
|
|
|
|
} bench;
|
|
|
|
#endif
|
2019-06-14 18:10:16 +02:00
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
std::array<double, slaposCount + slapsCount> step_times {};
|
|
|
|
|
|
|
|
auto apply_steps_on_objects =
|
2019-11-12 16:53:47 +01:00
|
|
|
[this, &st, &printsteps, &step_times, &bench]
|
2019-08-16 16:17:37 +02:00
|
|
|
(const std::vector<SLAPrintObjectStep> &steps)
|
|
|
|
{
|
2019-11-12 16:53:47 +01:00
|
|
|
double incr = 0;
|
2019-06-14 18:10:16 +02:00
|
|
|
for (SLAPrintObject *po : m_objects) {
|
2019-08-16 16:31:05 +02:00
|
|
|
for (SLAPrintObjectStep step : steps) {
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-06-14 18:10:16 +02:00
|
|
|
// Cancellation checking. Each step will check for
|
|
|
|
// cancellation on its own and return earlier gracefully.
|
|
|
|
// Just after it returns execution gets to this point and
|
|
|
|
// throws the canceled signal.
|
2019-02-20 11:58:09 +01:00
|
|
|
throw_if_canceled();
|
2018-11-09 18:32:35 +01:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
st += incr;
|
2018-11-21 12:10:10 +01:00
|
|
|
|
2019-08-16 16:31:05 +02:00
|
|
|
if (po->m_stepmask[step] && po->set_started(step)) {
|
2019-11-12 16:53:47 +01:00
|
|
|
m_report_status(*this, st, printsteps.label(step));
|
2019-08-16 16:17:37 +02:00
|
|
|
bench.start();
|
2019-11-12 16:53:47 +01:00
|
|
|
printsteps.execute(step, *po);
|
2019-08-16 16:17:37 +02:00
|
|
|
bench.stop();
|
2019-08-16 16:31:05 +02:00
|
|
|
step_times[step] += bench.getElapsedSec();
|
2019-02-20 11:58:09 +01:00
|
|
|
throw_if_canceled();
|
2019-08-16 16:31:05 +02:00
|
|
|
po->set_done(step);
|
2019-02-20 11:58:09 +01:00
|
|
|
}
|
2020-04-13 12:31:37 +02:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
incr = printsteps.progressrange(step);
|
2019-02-20 11:58:09 +01:00
|
|
|
}
|
2018-11-09 18:32:35 +01:00
|
|
|
}
|
2018-11-13 11:53:54 +01:00
|
|
|
};
|
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
apply_steps_on_objects(level1_obj_steps);
|
|
|
|
apply_steps_on_objects(level2_obj_steps);
|
|
|
|
|
2018-11-15 18:05:47 +01:00
|
|
|
// this would disable the rasterization step
|
2019-08-16 16:17:37 +02:00
|
|
|
// std::fill(m_stepmask.begin(), m_stepmask.end(), false);
|
2019-11-12 16:53:47 +01:00
|
|
|
|
|
|
|
st = Steps::max_objstatus;
|
2019-08-16 16:31:05 +02:00
|
|
|
for(SLAPrintStep currentstep : print_steps) {
|
2018-11-13 11:53:54 +01:00
|
|
|
throw_if_canceled();
|
|
|
|
|
2019-08-16 16:17:37 +02:00
|
|
|
if (m_stepmask[currentstep] && set_started(currentstep)) {
|
2019-11-12 16:53:47 +01:00
|
|
|
m_report_status(*this, st, printsteps.label(currentstep));
|
2019-08-16 16:17:37 +02:00
|
|
|
bench.start();
|
2019-11-12 16:53:47 +01:00
|
|
|
printsteps.execute(currentstep);
|
2019-08-16 16:17:37 +02:00
|
|
|
bench.stop();
|
|
|
|
step_times[slaposCount + currentstep] += bench.getElapsedSec();
|
2018-12-20 16:25:32 +01:00
|
|
|
throw_if_canceled();
|
2018-11-13 11:53:54 +01:00
|
|
|
set_done(currentstep);
|
|
|
|
}
|
2020-04-13 12:31:37 +02:00
|
|
|
|
2019-11-12 16:53:47 +01:00
|
|
|
st += printsteps.progressrange(currentstep);
|
2018-11-13 11:53:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If everything vent well
|
2019-04-02 10:54:14 +02:00
|
|
|
m_report_status(*this, 100, L("Slicing done"));
|
2019-08-16 16:17:37 +02:00
|
|
|
|
|
|
|
#ifdef SLAPRINT_DO_BENCHMARK
|
|
|
|
std::string csvbenchstr;
|
|
|
|
for (size_t i = 0; i < size_t(slaposCount); ++i)
|
2019-11-12 16:53:47 +01:00
|
|
|
csvbenchstr += printsteps.label(SLAPrintObjectStep(i)) + ";";
|
2019-08-16 16:17:37 +02:00
|
|
|
|
|
|
|
for (size_t i = 0; i < size_t(slapsCount); ++i)
|
2019-11-12 16:53:47 +01:00
|
|
|
csvbenchstr += printsteps.label(SLAPrintStep(i)) + ";";
|
2019-08-16 16:17:37 +02:00
|
|
|
|
|
|
|
csvbenchstr += "\n";
|
|
|
|
for (double t : step_times) csvbenchstr += std::to_string(t) + ";";
|
|
|
|
|
|
|
|
std::cout << "Performance stats: \n" << csvbenchstr << std::endl;
|
|
|
|
#endif
|
|
|
|
|
2018-11-08 20:18:40 +01:00
|
|
|
}
|
|
|
|
|
2019-04-05 11:56:11 +02:00
|
|
|
bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys, bool &invalidate_all_model_objects)
|
2018-11-21 17:35:35 +01:00
|
|
|
{
|
|
|
|
if (opt_keys.empty())
|
|
|
|
return false;
|
|
|
|
|
2019-04-03 17:34:46 +02:00
|
|
|
static std::unordered_set<std::string> steps_full = {
|
|
|
|
"initial_layer_height",
|
|
|
|
"material_correction",
|
2019-04-09 13:42:32 +02:00
|
|
|
"relative_correction",
|
|
|
|
"absolute_correction",
|
2020-02-06 14:09:48 +01:00
|
|
|
"elefant_foot_compensation",
|
2020-02-06 16:28:02 +01:00
|
|
|
"elefant_foot_min_width",
|
2019-04-09 13:42:32 +02:00
|
|
|
"gamma_correction"
|
2019-04-03 17:34:46 +02:00
|
|
|
};
|
|
|
|
|
2018-11-21 17:35:35 +01:00
|
|
|
// Cache the plenty of parameters, which influence the final rasterization only,
|
|
|
|
// or they are only notes not influencing the rasterization step.
|
|
|
|
static std::unordered_set<std::string> steps_rasterize = {
|
2019-08-20 17:24:48 +02:00
|
|
|
"min_exposure_time",
|
|
|
|
"max_exposure_time",
|
2018-11-21 17:35:35 +01:00
|
|
|
"exposure_time",
|
2019-08-20 17:24:48 +02:00
|
|
|
"min_initial_exposure_time",
|
|
|
|
"max_initial_exposure_time",
|
2018-11-21 17:35:35 +01:00
|
|
|
"initial_exposure_time",
|
|
|
|
"display_width",
|
|
|
|
"display_height",
|
|
|
|
"display_pixels_x",
|
|
|
|
"display_pixels_y",
|
2019-05-18 23:21:59 +02:00
|
|
|
"display_mirror_x",
|
|
|
|
"display_mirror_y",
|
2019-04-03 17:34:46 +02:00
|
|
|
"display_orientation"
|
2018-11-21 17:35:35 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static std::unordered_set<std::string> steps_ignore = {
|
|
|
|
"bed_shape",
|
|
|
|
"max_print_height",
|
|
|
|
"printer_technology",
|
2019-02-18 16:04:55 +01:00
|
|
|
"output_filename_format",
|
2019-04-03 18:23:29 +02:00
|
|
|
"fast_tilt_time",
|
|
|
|
"slow_tilt_time",
|
2019-09-26 16:33:55 +02:00
|
|
|
"area_fill",
|
|
|
|
"bottle_cost",
|
|
|
|
"bottle_volume",
|
|
|
|
"bottle_weight",
|
|
|
|
"material_density"
|
2018-11-21 17:35:35 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<SLAPrintStep> steps;
|
|
|
|
std::vector<SLAPrintObjectStep> osteps;
|
|
|
|
bool invalidated = false;
|
|
|
|
|
|
|
|
for (const t_config_option_key &opt_key : opt_keys) {
|
|
|
|
if (steps_rasterize.find(opt_key) != steps_rasterize.end()) {
|
|
|
|
// These options only affect the final rasterization, or they are just notes without influence on the output,
|
|
|
|
// so there is nothing to invalidate.
|
2019-03-27 10:59:29 +01:00
|
|
|
steps.emplace_back(slapsMergeSlicesAndEval);
|
2018-11-21 17:35:35 +01:00
|
|
|
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
|
|
|
// These steps have no influence on the output. Just ignore them.
|
2019-04-03 17:34:46 +02:00
|
|
|
} else if (steps_full.find(opt_key) != steps_full.end()) {
|
2019-03-27 10:59:29 +01:00
|
|
|
steps.emplace_back(slapsMergeSlicesAndEval);
|
2018-11-21 17:35:35 +01:00
|
|
|
osteps.emplace_back(slaposObjectSlice);
|
2019-04-05 11:56:11 +02:00
|
|
|
invalidate_all_model_objects = true;
|
2018-11-21 17:35:35 +01:00
|
|
|
} else {
|
|
|
|
// All values should be covered.
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort_remove_duplicates(steps);
|
|
|
|
for (SLAPrintStep step : steps)
|
|
|
|
invalidated |= this->invalidate_step(step);
|
|
|
|
sort_remove_duplicates(osteps);
|
|
|
|
for (SLAPrintObjectStep ostep : osteps)
|
|
|
|
for (SLAPrintObject *object : m_objects)
|
|
|
|
invalidated |= object->invalidate_step(ostep);
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
2018-12-11 17:49:31 +01:00
|
|
|
// Returns true if an object step is done on all objects and there's at least one object.
|
|
|
|
bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
|
|
|
{
|
|
|
|
if (m_objects.empty())
|
|
|
|
return false;
|
|
|
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
|
|
|
for (const SLAPrintObject *object : m_objects)
|
2019-02-21 08:44:07 +01:00
|
|
|
if (! object->is_step_done_unguarded(step))
|
2018-12-11 17:49:31 +01:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-09 13:12:55 +02:00
|
|
|
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
|
|
|
|
: Inherited(print, model_object)
|
|
|
|
, m_stepmask(slaposCount, true)
|
|
|
|
, m_transformed_rmesh([this](TriangleMesh &obj) {
|
|
|
|
obj = m_model_object->raw_mesh();
|
|
|
|
if (!obj.empty()) {
|
|
|
|
obj.transform(m_trafo);
|
|
|
|
obj.require_shared_vertices();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
{}
|
2018-11-09 18:32:35 +01:00
|
|
|
|
|
|
|
SLAPrintObject::~SLAPrintObject() {}
|
|
|
|
|
2019-05-22 16:43:14 +02:00
|
|
|
// Called by SLAPrint::apply().
|
2018-11-21 17:35:35 +01:00
|
|
|
// This method only accepts SLAPrintObjectConfig option keys.
|
|
|
|
bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
|
|
|
{
|
|
|
|
if (opt_keys.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<SLAPrintObjectStep> steps;
|
|
|
|
bool invalidated = false;
|
|
|
|
for (const t_config_option_key &opt_key : opt_keys) {
|
2019-11-06 13:38:43 +01:00
|
|
|
if ( opt_key == "hollowing_enable"
|
2019-11-07 09:34:34 +01:00
|
|
|
|| opt_key == "hollowing_min_thickness"
|
2019-11-08 09:21:30 +01:00
|
|
|
|| opt_key == "hollowing_quality"
|
2019-11-08 16:51:43 +01:00
|
|
|
|| opt_key == "hollowing_closing_distance"
|
2019-11-07 09:34:34 +01:00
|
|
|
) {
|
2019-11-06 13:38:43 +01:00
|
|
|
steps.emplace_back(slaposHollowing);
|
|
|
|
} else if (
|
|
|
|
opt_key == "layer_height"
|
2019-03-01 17:53:02 +01:00
|
|
|
|| opt_key == "faded_layers"
|
2019-03-22 15:48:20 +01:00
|
|
|
|| opt_key == "pad_enable"
|
|
|
|
|| opt_key == "pad_wall_thickness"
|
|
|
|
|| opt_key == "supports_enable"
|
|
|
|
|| opt_key == "support_object_elevation"
|
2019-08-28 11:32:49 +02:00
|
|
|
|| opt_key == "pad_around_object"
|
2019-09-24 15:15:49 +02:00
|
|
|
|| opt_key == "pad_around_object_everywhere"
|
2019-03-01 17:53:02 +01:00
|
|
|
|| opt_key == "slice_closing_radius") {
|
2019-04-03 18:23:29 +02:00
|
|
|
steps.emplace_back(slaposObjectSlice);
|
2019-02-19 16:34:52 +01:00
|
|
|
} else if (
|
2019-03-22 15:48:20 +01:00
|
|
|
|
|
|
|
opt_key == "support_points_density_relative"
|
2019-02-19 16:34:52 +01:00
|
|
|
|| opt_key == "support_points_minimal_distance") {
|
2018-11-30 14:26:41 +01:00
|
|
|
steps.emplace_back(slaposSupportPoints);
|
2019-04-03 18:23:29 +02:00
|
|
|
} else if (
|
2018-11-30 14:26:41 +01:00
|
|
|
opt_key == "support_head_front_diameter"
|
2018-11-21 17:35:35 +01:00
|
|
|
|| opt_key == "support_head_penetration"
|
|
|
|
|| opt_key == "support_head_width"
|
2018-11-29 11:45:02 +01:00
|
|
|
|| opt_key == "support_pillar_diameter"
|
2020-07-08 11:31:01 +02:00
|
|
|
|| opt_key == "support_small_pillar_diameter_percent"
|
2020-03-02 12:43:00 +01:00
|
|
|
|| opt_key == "support_max_bridges_on_pillar"
|
2019-01-09 12:21:43 +01:00
|
|
|
|| opt_key == "support_pillar_connection_mode"
|
2019-02-05 11:16:03 +01:00
|
|
|
|| opt_key == "support_buildplate_only"
|
2018-11-29 11:45:02 +01:00
|
|
|
|| opt_key == "support_base_diameter"
|
2018-11-21 17:35:35 +01:00
|
|
|
|| opt_key == "support_base_height"
|
|
|
|
|| opt_key == "support_critical_angle"
|
|
|
|
|| opt_key == "support_max_bridge_length"
|
2019-03-08 11:39:34 +01:00
|
|
|
|| opt_key == "support_max_pillar_link_distance"
|
2019-06-11 17:57:39 +02:00
|
|
|
|| opt_key == "support_base_safety_distance"
|
2019-03-22 15:48:20 +01:00
|
|
|
) {
|
2018-11-21 17:35:35 +01:00
|
|
|
steps.emplace_back(slaposSupportTree);
|
|
|
|
} else if (
|
2019-03-22 15:48:20 +01:00
|
|
|
opt_key == "pad_wall_height"
|
2019-09-24 15:15:49 +02:00
|
|
|
|| opt_key == "pad_brim_size"
|
2018-11-21 17:35:35 +01:00
|
|
|
|| opt_key == "pad_max_merge_distance"
|
2019-02-25 16:04:46 +01:00
|
|
|
|| opt_key == "pad_wall_slope"
|
2019-06-11 17:57:39 +02:00
|
|
|
|| opt_key == "pad_edge_radius"
|
2019-06-12 13:15:42 +02:00
|
|
|
|| opt_key == "pad_object_gap"
|
2019-06-11 17:57:39 +02:00
|
|
|
|| opt_key == "pad_object_connector_stride"
|
|
|
|
|| opt_key == "pad_object_connector_width"
|
|
|
|
|| opt_key == "pad_object_connector_penetration"
|
|
|
|
) {
|
2019-09-24 15:15:49 +02:00
|
|
|
steps.emplace_back(slaposPad);
|
2018-11-21 17:35:35 +01:00
|
|
|
} else {
|
|
|
|
// All keys should be covered.
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort_remove_duplicates(steps);
|
|
|
|
for (SLAPrintObjectStep step : steps)
|
|
|
|
invalidated |= this->invalidate_step(step);
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
|
|
|
{
|
|
|
|
bool invalidated = Inherited::invalidate_step(step);
|
|
|
|
// propagate to dependent steps
|
2019-11-05 14:48:00 +01:00
|
|
|
if (step == slaposHollowing) {
|
2018-11-21 17:35:35 +01:00
|
|
|
invalidated |= this->invalidate_all_steps();
|
2020-01-31 08:58:21 +01:00
|
|
|
} else if (step == slaposDrillHoles) {
|
|
|
|
invalidated |= this->invalidate_steps({ slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
2019-11-05 14:48:00 +01:00
|
|
|
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
2020-01-31 08:58:21 +01:00
|
|
|
} else if (step == slaposObjectSlice) {
|
2019-11-05 14:48:00 +01:00
|
|
|
invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
|
|
|
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
2018-11-21 17:35:35 +01:00
|
|
|
} else if (step == slaposSupportPoints) {
|
2019-09-24 15:15:49 +02:00
|
|
|
invalidated |= this->invalidate_steps({ slaposSupportTree, slaposPad, slaposSliceSupports });
|
2019-03-27 10:59:29 +01:00
|
|
|
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
2018-11-21 17:35:35 +01:00
|
|
|
} else if (step == slaposSupportTree) {
|
2019-09-24 15:15:49 +02:00
|
|
|
invalidated |= this->invalidate_steps({ slaposPad, slaposSliceSupports });
|
2019-03-27 10:59:29 +01:00
|
|
|
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
2019-09-24 15:15:49 +02:00
|
|
|
} else if (step == slaposPad) {
|
2019-03-27 10:59:29 +01:00
|
|
|
invalidated |= this->invalidate_steps({slaposSliceSupports});
|
|
|
|
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
2018-11-21 17:35:35 +01:00
|
|
|
} else if (step == slaposSliceSupports) {
|
2019-03-27 10:59:29 +01:00
|
|
|
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
2018-11-21 17:35:35 +01:00
|
|
|
}
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SLAPrintObject::invalidate_all_steps()
|
|
|
|
{
|
|
|
|
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
|
|
|
}
|
|
|
|
|
2018-11-16 17:25:23 +01:00
|
|
|
double SLAPrintObject::get_elevation() const {
|
2019-08-06 16:51:32 +02:00
|
|
|
if (is_zero_elevation(m_config)) return 0.;
|
|
|
|
|
|
|
|
bool en = m_config.supports_enable.getBool();
|
|
|
|
|
2019-06-17 13:02:49 +02:00
|
|
|
double ret = en ? m_config.support_object_elevation.getFloat() : 0.;
|
2018-11-20 16:12:04 +01:00
|
|
|
|
|
|
|
if(m_config.pad_enable.getBool()) {
|
|
|
|
// Normally the elevation for the pad itself would be the thickness of
|
|
|
|
// its walls but currently it is half of its thickness. Whatever it
|
|
|
|
// will be in the future, we provide the config to the get_pad_elevation
|
|
|
|
// method and we will have the correct value
|
2019-09-24 15:15:49 +02:00
|
|
|
sla::PadConfig pcfg = make_pad_cfg(m_config);
|
|
|
|
if(!pcfg.embed_object) ret += pcfg.required_elevation();
|
2018-11-20 16:12:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2018-11-16 17:25:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 13:57:13 +01:00
|
|
|
double SLAPrintObject::get_current_elevation() const
|
|
|
|
{
|
2019-08-06 16:51:32 +02:00
|
|
|
if (is_zero_elevation(m_config)) return 0.;
|
|
|
|
|
2018-11-22 13:57:13 +01:00
|
|
|
bool has_supports = is_step_done(slaposSupportTree);
|
2019-09-24 15:15:49 +02:00
|
|
|
bool has_pad = is_step_done(slaposPad);
|
2018-12-21 14:32:11 +01:00
|
|
|
|
|
|
|
if(!has_supports && !has_pad)
|
|
|
|
return 0;
|
2019-06-11 12:40:07 +02:00
|
|
|
else if(has_supports && !has_pad) {
|
2019-06-11 18:19:58 +02:00
|
|
|
return m_config.support_object_elevation.getFloat();
|
2019-06-11 12:40:07 +02:00
|
|
|
}
|
2018-11-22 13:57:13 +01:00
|
|
|
|
2018-12-21 14:32:11 +01:00
|
|
|
return get_elevation();
|
2018-11-22 13:57:13 +01:00
|
|
|
}
|
|
|
|
|
2019-04-04 10:52:14 +02:00
|
|
|
Vec3d SLAPrint::relative_correction() const
|
|
|
|
{
|
|
|
|
Vec3d corr(1., 1., 1.);
|
|
|
|
|
2019-04-29 17:07:55 +02:00
|
|
|
if(printer_config().relative_correction.values.size() >= 2) {
|
2019-04-09 18:15:25 +02:00
|
|
|
corr(X) = printer_config().relative_correction.values[0];
|
|
|
|
corr(Y) = printer_config().relative_correction.values[0];
|
2019-04-29 17:07:55 +02:00
|
|
|
corr(Z) = printer_config().relative_correction.values.back();
|
2019-08-16 16:17:37 +02:00
|
|
|
}
|
2019-04-04 10:52:14 +02:00
|
|
|
|
2019-04-29 17:07:55 +02:00
|
|
|
if(material_config().material_correction.values.size() >= 2) {
|
2019-04-09 18:15:25 +02:00
|
|
|
corr(X) *= material_config().material_correction.values[0];
|
|
|
|
corr(Y) *= material_config().material_correction.values[0];
|
2019-04-29 17:07:55 +02:00
|
|
|
corr(Z) *= material_config().material_correction.values.back();
|
2019-04-04 10:52:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return corr;
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:21:57 +01:00
|
|
|
namespace { // dummy empty static containers for return values in some methods
|
|
|
|
const std::vector<ExPolygons> EMPTY_SLICES;
|
|
|
|
const TriangleMesh EMPTY_MESH;
|
2019-03-22 15:31:38 +01:00
|
|
|
const ExPolygons EMPTY_SLICE;
|
2019-07-09 13:12:55 +02:00
|
|
|
const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS;
|
2018-11-21 15:21:57 +01:00
|
|
|
}
|
2018-11-16 17:25:23 +01:00
|
|
|
|
2019-03-25 19:02:05 +01:00
|
|
|
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
|
|
|
|
|
2019-02-17 13:05:22 +01:00
|
|
|
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
|
2018-12-21 12:35:20 +01:00
|
|
|
{
|
2019-09-26 09:42:08 +02:00
|
|
|
return m_supportdata? m_supportdata->pts : EMPTY_SUPPORT_POINTS;
|
2018-12-21 12:35:20 +01:00
|
|
|
}
|
|
|
|
|
2019-03-22 15:31:38 +01:00
|
|
|
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
|
2019-03-20 18:03:11 +01:00
|
|
|
{
|
2019-03-22 15:31:38 +01:00
|
|
|
// assert(is_step_done(slaposSliceSupports));
|
|
|
|
if (!m_supportdata) return EMPTY_SLICES;
|
|
|
|
return m_supportdata->support_slices;
|
|
|
|
}
|
2019-03-21 15:16:33 +01:00
|
|
|
|
2019-03-26 10:57:45 +01:00
|
|
|
const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
|
2019-03-22 15:31:38 +01:00
|
|
|
{
|
2020-02-14 10:17:55 +01:00
|
|
|
size_t idx = o == soModel ? m_model_slices_idx : m_support_slices_idx;
|
2019-03-26 10:57:45 +01:00
|
|
|
|
|
|
|
if(m_po == nullptr) return EMPTY_SLICE;
|
2019-03-20 18:03:11 +01:00
|
|
|
|
2019-03-26 10:57:45 +01:00
|
|
|
const std::vector<ExPolygons>& v = o == soModel? m_po->get_model_slices() :
|
|
|
|
m_po->get_support_slices();
|
2019-03-21 15:16:33 +01:00
|
|
|
|
2019-03-22 15:31:38 +01:00
|
|
|
return idx >= v.size() ? EMPTY_SLICE : v[idx];
|
2019-03-20 18:03:11 +01:00
|
|
|
}
|
|
|
|
|
2018-11-17 17:23:56 +01:00
|
|
|
bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const
|
|
|
|
{
|
|
|
|
switch (step) {
|
2020-02-03 15:42:54 +01:00
|
|
|
case slaposDrillHoles:
|
2020-01-30 17:58:40 +01:00
|
|
|
return m_hollowing_data && !m_hollowing_data->hollow_mesh_with_holes.empty();
|
2018-11-17 17:23:56 +01:00
|
|
|
case slaposSupportTree:
|
2018-11-27 13:52:42 +01:00
|
|
|
return ! this->support_mesh().empty();
|
2019-09-24 15:15:49 +02:00
|
|
|
case slaposPad:
|
2018-11-27 13:52:42 +01:00
|
|
|
return ! this->pad_mesh().empty();
|
|
|
|
default:
|
2018-11-17 17:23:56 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
|
|
|
|
{
|
2018-11-27 13:52:42 +01:00
|
|
|
switch (step) {
|
|
|
|
case slaposSupportTree:
|
|
|
|
return this->support_mesh();
|
2019-09-24 15:15:49 +02:00
|
|
|
case slaposPad:
|
2018-11-27 13:52:42 +01:00
|
|
|
return this->pad_mesh();
|
2020-02-03 15:42:54 +01:00
|
|
|
case slaposDrillHoles:
|
2020-01-23 11:22:06 +01:00
|
|
|
if (m_hollowing_data)
|
2021-03-02 17:03:33 +01:00
|
|
|
return get_mesh_to_print();
|
2020-01-23 11:22:06 +01:00
|
|
|
[[fallthrough]];
|
2018-11-27 13:52:42 +01:00
|
|
|
default:
|
|
|
|
return TriangleMesh();
|
|
|
|
}
|
2018-11-17 17:23:56 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 15:21:57 +01:00
|
|
|
const TriangleMesh& SLAPrintObject::support_mesh() const
|
|
|
|
{
|
2019-09-26 09:42:08 +02:00
|
|
|
sla::SupportTree::UPtr &stree = m_supportdata->support_tree_ptr;
|
|
|
|
|
|
|
|
if(m_config.supports_enable.getBool() && m_supportdata && stree)
|
|
|
|
return stree->retrieve_mesh(sla::MeshType::Support);
|
|
|
|
|
2018-11-21 15:21:57 +01:00
|
|
|
return EMPTY_MESH;
|
2018-11-13 17:45:44 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 15:21:57 +01:00
|
|
|
const TriangleMesh& SLAPrintObject::pad_mesh() const
|
2018-11-13 17:45:44 +01:00
|
|
|
{
|
2019-09-26 09:42:08 +02:00
|
|
|
sla::SupportTree::UPtr &stree = m_supportdata->support_tree_ptr;
|
|
|
|
|
|
|
|
if(m_config.pad_enable.getBool() && m_supportdata && stree)
|
|
|
|
return stree->retrieve_mesh(sla::MeshType::Pad);
|
2018-11-14 18:04:43 +01:00
|
|
|
|
2018-11-30 14:26:41 +01:00
|
|
|
return EMPTY_MESH;
|
2018-11-13 17:45:44 +01:00
|
|
|
}
|
|
|
|
|
2019-11-06 14:25:03 +01:00
|
|
|
const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const
|
|
|
|
{
|
2020-12-17 16:38:04 +01:00
|
|
|
if (m_hollowing_data && m_hollowing_data->interior &&
|
|
|
|
m_config.hollowing_enable.getBool())
|
|
|
|
return sla::get_mesh(*m_hollowing_data->interior);
|
2019-11-06 14:25:03 +01:00
|
|
|
|
|
|
|
return EMPTY_MESH;
|
|
|
|
}
|
|
|
|
|
2018-11-15 18:05:47 +01:00
|
|
|
const TriangleMesh &SLAPrintObject::transformed_mesh() const {
|
|
|
|
// we need to transform the raw mesh...
|
|
|
|
// currently all the instances share the same x and y rotation and scaling
|
|
|
|
// so we have to extract those from e.g. the first instance and apply to the
|
|
|
|
// raw mesh. This is also true for the support points.
|
|
|
|
// BUT: when the support structure is spawned for each instance than it has
|
|
|
|
// to omit the X, Y rotation and scaling as those have been already applied
|
|
|
|
// or apply an inverse transformation on the support structure after it
|
|
|
|
// has been created.
|
|
|
|
|
2018-11-21 15:21:57 +01:00
|
|
|
return m_transformed_rmesh.get();
|
2018-11-15 18:05:47 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 15:55:37 +01:00
|
|
|
sla::SupportPoints SLAPrintObject::transformed_support_points() const
|
|
|
|
{
|
|
|
|
assert(m_model_object != nullptr);
|
2020-01-08 10:10:24 +01:00
|
|
|
auto spts = m_model_object->sla_support_points;
|
|
|
|
auto tr = trafo().cast<float>();
|
|
|
|
for (sla::SupportPoint& suppt : spts) {
|
|
|
|
suppt.pos = tr * suppt.pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
return spts;
|
2019-11-13 15:55:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const
|
|
|
|
{
|
|
|
|
assert(m_model_object != nullptr);
|
2020-01-08 10:10:24 +01:00
|
|
|
auto pts = m_model_object->sla_drain_holes;
|
|
|
|
auto tr = trafo().cast<float>();
|
2020-01-23 12:14:43 +01:00
|
|
|
auto sc = m_model_object->instances.front()->get_scaling_factor().cast<float>();
|
2020-01-08 10:10:24 +01:00
|
|
|
for (sla::DrainHole &hl : pts) {
|
|
|
|
hl.pos = tr * hl.pos;
|
2020-01-08 10:49:54 +01:00
|
|
|
hl.normal = tr * hl.normal - tr.translation();
|
2020-01-23 12:14:43 +01:00
|
|
|
|
|
|
|
// The normal scales as a covector (and we must also
|
|
|
|
// undo the damage already done).
|
|
|
|
hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)),
|
|
|
|
hl.normal(1)/(sc(1)*sc(1)),
|
|
|
|
hl.normal(2)/(sc(2)*sc(2)));
|
2020-08-19 15:24:55 +02:00
|
|
|
|
|
|
|
// Now shift the hole a bit above the object and make it deeper to
|
|
|
|
// compensate for it. This is to avoid problems when the hole is placed
|
|
|
|
// on (nearly) flat surface.
|
|
|
|
hl.pos -= hl.normal.normalized() * sla::HoleStickOutLength;
|
|
|
|
hl.height += sla::HoleStickOutLength;
|
2020-01-08 10:10:24 +01:00
|
|
|
}
|
2020-01-23 12:14:43 +01:00
|
|
|
|
2020-01-08 10:10:24 +01:00
|
|
|
return pts;
|
2019-11-13 15:55:37 +01:00
|
|
|
}
|
|
|
|
|
2019-02-12 16:34:42 +01:00
|
|
|
DynamicConfig SLAPrintStatistics::config() const
|
|
|
|
{
|
|
|
|
DynamicConfig config;
|
2019-09-11 12:13:59 +02:00
|
|
|
const std::string print_time = Slic3r::short_time(get_time_dhms(float(this->estimated_print_time)));
|
2019-02-12 16:34:42 +01:00
|
|
|
config.set_key_value("print_time", new ConfigOptionString(print_time));
|
2019-02-13 15:35:41 +01:00
|
|
|
config.set_key_value("objects_used_material", new ConfigOptionFloat(this->objects_used_material));
|
|
|
|
config.set_key_value("support_used_material", new ConfigOptionFloat(this->support_used_material));
|
2019-02-12 16:34:42 +01:00
|
|
|
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
|
|
|
|
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
DynamicConfig SLAPrintStatistics::placeholders()
|
|
|
|
{
|
|
|
|
DynamicConfig config;
|
|
|
|
for (const std::string &key : {
|
2019-02-13 15:35:41 +01:00
|
|
|
"print_time", "total_cost", "total_weight",
|
|
|
|
"objects_used_material", "support_used_material" })
|
2019-02-12 16:34:42 +01:00
|
|
|
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
|
2019-04-02 10:54:14 +02:00
|
|
|
|
|
|
|
return config;
|
2019-02-12 16:34:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) const
|
|
|
|
{
|
|
|
|
std::string final_path;
|
|
|
|
try {
|
|
|
|
boost::filesystem::path path(path_in);
|
|
|
|
DynamicConfig cfg = this->config();
|
|
|
|
PlaceholderParser pp;
|
|
|
|
std::string new_stem = pp.process(path.stem().string(), 0, &cfg);
|
|
|
|
final_path = (path.parent_path() / (new_stem + path.extension().string())).string();
|
|
|
|
}
|
|
|
|
catch (const std::exception &ex) {
|
|
|
|
BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what();
|
|
|
|
final_path = path_in;
|
|
|
|
}
|
|
|
|
return final_path;
|
|
|
|
}
|
|
|
|
|
2019-07-30 17:55:22 +02:00
|
|
|
void SLAPrint::StatusReporter::operator()(SLAPrint & p,
|
|
|
|
double st,
|
|
|
|
const std::string &msg,
|
|
|
|
unsigned flags,
|
|
|
|
const std::string &logmsg)
|
2019-04-02 10:54:14 +02:00
|
|
|
{
|
|
|
|
m_st = st;
|
2019-07-30 17:55:22 +02:00
|
|
|
BOOST_LOG_TRIVIAL(info)
|
|
|
|
<< st << "% " << msg << (logmsg.empty() ? "" : ": ") << logmsg
|
|
|
|
<< log_memory_info();
|
2019-08-16 16:17:37 +02:00
|
|
|
|
2019-04-02 10:54:14 +02:00
|
|
|
p.set_status(int(std::round(st)), msg, flags);
|
|
|
|
}
|
|
|
|
|
2018-11-09 12:02:42 +01:00
|
|
|
} // namespace Slic3r
|