Merge remote-tracking branch 'origin/dev_native' into tm_sla_supports

This commit is contained in:
tamasmeszaros 2018-11-07 15:30:41 +01:00
commit 97b3d94760
55 changed files with 1903 additions and 808 deletions

View file

@ -439,14 +439,16 @@ void ConfigBase::load_from_gcode_file(const std::string &file)
ifs.read(data.data(), data_length);
ifs.close();
load_from_gcode_string(data.data());
size_t key_value_pairs = load_from_gcode_string(data.data());
if (key_value_pairs < 80)
throw std::runtime_error((boost::format("Suspiciously low number of configuration values extracted from %1: %2") % file % key_value_pairs).str());
}
// Load the config keys from the given string.
void ConfigBase::load_from_gcode_string(const char* str)
size_t ConfigBase::load_from_gcode_string(const char* str)
{
if (str == nullptr)
return;
return 0;
// Walk line by line in reverse until a non-configuration key appears.
char *data_start = const_cast<char*>(str);
@ -497,11 +499,8 @@ void ConfigBase::load_from_gcode_string(const char* str)
}
end = start;
}
if (num_key_value_pairs < 90) {
char msg[80];
sprintf(msg, "Suspiciously low number of configuration values extracted: %d", num_key_value_pairs);
throw std::runtime_error(msg);
}
return num_key_value_pairs;
}
void ConfigBase::save(const std::string &file) const

View file

@ -1119,7 +1119,8 @@ public:
void load(const std::string &file);
void load_from_ini(const std::string &file);
void load_from_gcode_file(const std::string &file);
void load_from_gcode_string(const char* str);
// Returns number of key/value pairs extracted.
size_t load_from_gcode_string(const char* str);
void load(const boost::property_tree::ptree &tree);
void save(const std::string &file) const;
@ -1237,6 +1238,7 @@ public:
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
t_config_option_keys keys() const override;
bool empty() const { return options.empty(); }
// Set a value for an opt_key. Returns true if the value did not exist yet.
// This DynamicConfig will take ownership of opt.

View file

@ -247,7 +247,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Only concentric fills are not sorted.
eec->no_sort = f->no_sort();
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines),
eec->entities, std::move(polylines),
is_bridge ?
erBridgeInfill :
(surface.is_solid() ?

View file

@ -813,7 +813,7 @@ namespace Slic3r {
std::vector<Vec3f> sla_support_points;
for (unsigned int i=0; i<object_data_points.size(); i+=3)
sla_support_points.push_back(Vec3f(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())));
sla_support_points.push_back(Vec3d(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())).cast<float>());
if (!sla_support_points.empty())
m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points));
@ -1608,10 +1608,10 @@ namespace Slic3r {
IdToObjectDataMap m_objects_data;
public:
bool save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config);
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
private:
bool _save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config);
bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
bool _add_relationships_file_to_archive(mz_zip_archive& archive);
bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model);
@ -1620,17 +1620,17 @@ namespace Slic3r {
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model);
};
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config)
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config)
{
clear_errors();
return _save_model_to_file(filename, model, print, export_print_config);
return _save_model_to_file(filename, model, config);
}
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config)
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
@ -1685,9 +1685,9 @@ namespace Slic3r {
}
// adds slic3r print config file
if (export_print_config)
if (config != nullptr)
{
if (!_add_print_config_file_to_archive(archive, print))
if (!_add_print_config_file_to_archive(archive, *config))
{
mz_zip_writer_end(&archive);
boost::filesystem::remove(filename);
@ -2017,13 +2017,15 @@ namespace Slic3r {
return true;
}
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print)
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
{
char buffer[1024];
sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
std::string out = buffer;
GCode::append_full_config(print, out);
for (const std::string &key : config.keys())
if (key != "compatible_printers")
out += "; " + key + " = " + config.serialize(key) + "\n";
if (!out.empty())
{
@ -2123,13 +2125,13 @@ namespace Slic3r {
return res;
}
bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config)
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
if ((path == nullptr) || (model == nullptr))
return false;
_3MF_Exporter exporter;
bool res = exporter.save_model_to_file(path, *model, *print, export_print_config);
bool res = exporter.save_model_to_file(path, *model, config);
if (!res)
exporter.log_errors();

View file

@ -4,14 +4,14 @@
namespace Slic3r {
class Model;
class Print;
class DynamicPrintConfig;
// Load the content of a 3mf file into the given model and preset bundle.
extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model);
// Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
extern bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config);
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config);
}; // namespace Slic3r

View file

@ -834,9 +834,9 @@ bool load_amf(const char *path, DynamicPrintConfig *config, Model *model)
return false;
}
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
if ((path == nullptr) || (model == nullptr))
return false;
// forces ".zip.amf" extension
@ -857,11 +857,13 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n";
if (export_print_config)
if (config != nullptr)
{
std::string config = "\n";
GCode::append_full_config(*print, config);
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(config) << "</metadata>\n";
std::string str_config = "\n";
for (const std::string &key : config->keys())
if (key != "compatible_printers")
str_config += "; " + key + " = " + config->serialize(key) + "\n";
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(str_config) << "</metadata>\n";
}
for (const auto &material : model->materials) {

View file

@ -4,14 +4,14 @@
namespace Slic3r {
class Model;
class Print;
class DynamicPrintConfig;
// Load the content of an amf file into the given model and configuration.
extern bool load_amf(const char *path, DynamicPrintConfig *config, Model *model);
// Save the given model and the config data contained in the given Print into an amf file.
// Save the given model and the config data into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
extern bool store_amf(const char *path, Model *model, Print* print, bool export_print_config);
extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config);
}; // namespace Slic3r

View file

@ -445,7 +445,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
} catch (std::exception &ex) {
} catch (std::exception & /* ex */) {
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
// Close and remove the file.
fclose(file);
@ -601,15 +601,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
this->apply_print_config(print.config());
this->set_extruders(print.extruders());
// Initialize colorprint.
m_colorprint_heights = cast<float>(print.config().colorprint_heights.values);
// Initialize autospeed.
{
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
for (auto object : printable_objects) {
for (size_t region_id = 0; region_id < print.regions().size(); ++region_id) {
auto region = print.regions()[region_id];
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const PrintRegion* region = print.regions()[region_id];
for (auto layer : object->layers()) {
auto layerm = layer->regions()[region_id];
const LayerRegion* layerm = layer->regions()[region_id];
if (region->config().get_abs_value("perimeter_speed" ) == 0 ||
region->config().get_abs_value("small_perimeter_speed" ) == 0 ||
region->config().get_abs_value("external_perimeter_speed" ) == 0 ||
@ -673,8 +676,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
auto region = print.regions()[region_id];
for (const PrintRegion* region : print.regions()) {
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
@ -1319,6 +1321,18 @@ void GCode::process_layer(
m_second_layer_things_done = true;
}
// Let's issue a filament change command if requested at this layer.
// In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all.
// (Layers can be close to each other, model could have been resliced with bigger layer height, ...).
bool colorprint_change = false;
while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) {
m_colorprint_heights.erase(m_colorprint_heights.begin());
colorprint_change = true;
}
if (colorprint_change)
gcode += "M600\n";
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
bool extrude_skirt =
@ -1442,7 +1456,7 @@ void GCode::process_layer(
};
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
const LayerRegion *layerm = layer.regions()[region_id];
const LayerRegion *layerm = (region_id < layer.regions().size()) ? layer.regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions()[region_id];

View file

@ -317,6 +317,9 @@ protected:
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
// Layer heights for colorprint - updated before the export and erased during the process
// so no toolchange occurs twice.
std::vector<float> m_colorprint_heights;
// Time estimators
GCodeTimeEstimator m_normal_time_estimator;

View file

@ -151,7 +151,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
@ -479,7 +479,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
@ -557,7 +557,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
unsigned int num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)

View file

@ -32,9 +32,8 @@ void Layer::make_slices()
slices = m_regions.front()->slices;
} else {
Polygons slices_p;
FOREACH_LAYERREGION(this, layerm) {
polygons_append(slices_p, to_polygons((*layerm)->slices));
}
for (LayerRegion *layerm : m_regions)
polygons_append(slices_p, to_polygons(layerm->slices));
slices = union_ex(slices_p);
}
@ -53,7 +52,7 @@ void Layer::make_slices()
// populate slices vector
for (size_t i : order)
this->slices.expolygons.push_back(STDMOVE(slices[i]));
this->slices.expolygons.push_back(std::move(slices[i]));
}
void Layer::merge_slices()
@ -63,10 +62,9 @@ void Layer::merge_slices()
// but use the non-split islands of a layer. For a single region print, these shall be equal.
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
} else {
FOREACH_LAYERREGION(this, layerm) {
for (LayerRegion *layerm : m_regions)
// without safety offset, artifacts are generated (GH #2494)
(*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
}
layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal);
}
}
@ -80,7 +78,7 @@ void Layer::make_perimeters()
// keep track of regions whose perimeters we have already generated
std::set<size_t> done;
FOREACH_LAYERREGION(this, layerm) {
for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) {
size_t region_id = layerm - m_regions.begin();
if (done.find(region_id) != done.end()) continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
@ -137,7 +135,7 @@ void Layer::make_perimeters()
// Separate the fill surfaces.
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
}
}
}

View file

@ -34,7 +34,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
// in place. However we're now only using its boundaries (which are invariant)
// so we're safe. This guarantees idempotence of prepare_infill() also in case
// that combine_infill() turns some fill_surface into VOID surfaces.
// Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces));
// Polygons fill_boundaries = to_polygons(std::move(this->fill_surfaces));
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
// Collect polygons per surface type.
std::vector<Polygons> polygons_by_surface;
@ -133,9 +133,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (internal_surface)
// Make a copy as the following line uses the move semantics.
internal.push_back(surface);
polygons_append(fill_boundaries, STDMOVE(surface.expolygon));
polygons_append(fill_boundaries, std::move(surface.expolygon));
} else if (internal_surface)
internal.push_back(STDMOVE(surface));
internal.push_back(std::move(surface));
}
}
@ -192,7 +192,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island]));
}
bridge_bboxes.push_back(get_extents(polys));
bridges_grown.push_back(STDMOVE(polys));
bridges_grown.push_back(std::move(polys));
}
}
@ -243,7 +243,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] != group_id)
continue;
initial.push_back(STDMOVE(bridges[i].expolygon));
initial.push_back(std::move(bridges[i].expolygon));
polygons_append(grown, bridges_grown[i]);
}
// detect bridge direction before merging grown surfaces otherwise adjacent bridges
@ -269,7 +269,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]);
}
fill_boundaries = STDMOVE(to_polygons(fill_boundaries_ex));
fill_boundaries = std::move(to_polygons(fill_boundaries_ex));
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
}
@ -284,7 +284,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
Surfaces new_surfaces;
{
// Merge top and bottom in a single collection.
surfaces_append(top, STDMOVE(bottom));
surfaces_append(top, std::move(bottom));
// Intersect the grown surfaces with the actual fill boundaries.
Polygons bottom_polygons = to_polygons(bottom);
for (size_t i = 0; i < top.size(); ++ i) {
@ -292,11 +292,11 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, STDMOVE(s1));
polygons_append(polys, std::move(s1));
for (size_t j = i + 1; j < top.size(); ++ j) {
Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
polygons_append(polys, std::move(s2));
s2.clear();
}
}
@ -306,7 +306,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
surfaces_append(
new_surfaces,
// Don't use a safety offset as fill_boundaries were already united using the safety offset.
STDMOVE(intersection_ex(polys, fill_boundaries, false)),
std::move(intersection_ex(polys, fill_boundaries, false)),
s1);
}
}
@ -318,20 +318,20 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, STDMOVE(s1));
polygons_append(polys, std::move(s1));
for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
polygons_append(polys, std::move(s2));
s2.clear();
}
}
ExPolygons new_expolys = diff_ex(polys, new_polygons);
polygons_append(new_polygons, to_polygons(new_expolys));
surfaces_append(new_surfaces, STDMOVE(new_expolys), s1);
surfaces_append(new_surfaces, std::move(new_expolys), s1);
}
this->fill_surfaces.surfaces = STDMOVE(new_surfaces);
this->fill_surfaces.surfaces = std::move(new_surfaces);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");

View file

@ -21,26 +21,58 @@ namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
ModelID ModelBase::s_last_id = 0;
size_t ModelBase::s_last_id = 0;
Model::Model(const Model &rhs)
Model& Model::assign_copy(const Model &rhs)
{
*this = rhs;
this->copy_id(rhs);
// copy materials
this->clear_materials();
this->materials = rhs.materials;
for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials) {
// Copy including the ID and m_model.
m.second = new ModelMaterial(*m.second);
m.second->set_model(this);
}
// copy objects
this->clear_objects();
this->objects.reserve(rhs.objects.size());
for (const ModelObject *model_object : rhs.objects) {
// Copy including the ID, leave ID set to invalid (zero).
auto mo = ModelObject::new_copy(*model_object);
mo->set_model(this);
this->objects.emplace_back(mo);
}
return *this;
}
Model& Model::operator=(const Model &rhs)
Model& Model::assign_copy(Model &&rhs)
{
m_id = rhs.m_id;
// copy materials
for (const auto &m : rhs.materials)
this->add_material(m.first, *m.second);
// copy objects
this->objects.reserve(rhs.objects.size());
for (const ModelObject *o : rhs.objects)
this->add_object(*o, true);
this->copy_id(rhs);
// Move materials, adjust the parent pointer.
this->clear_materials();
this->materials = std::move(rhs.materials);
for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials)
m.second->set_model(this);
rhs.materials.clear();
// Move objects, adjust the parent pointer.
this->clear_objects();
this->objects = std::move(rhs.objects);
for (ModelObject *model_object : this->objects)
model_object->set_model(this);
rhs.objects.clear();
return *this;
}
void Model::assign_new_unique_ids_recursive()
{
this->set_new_unique_id();
for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials)
m.second->assign_new_unique_ids_recursive();
for (ModelObject *model_object : this->objects)
model_object->assign_new_unique_ids_recursive();
}
Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances)
{
Model model;
@ -150,9 +182,10 @@ ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh
return new_object;
}
ModelObject* Model::add_object(const ModelObject &other, bool copy_volumes)
ModelObject* Model::add_object(const ModelObject &other)
{
ModelObject* new_object = new ModelObject(this, other, copy_volumes);
ModelObject* new_object = ModelObject::new_clone(other);
new_object->set_model(this);
this->objects.push_back(new_object);
return new_object;
}
@ -164,21 +197,36 @@ void Model::delete_object(size_t idx)
this->objects.erase(i);
}
void Model::delete_object(ModelObject* object)
bool Model::delete_object(ModelObject* object)
{
if (object == nullptr)
return;
for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it)
{
ModelObject* obj = *it;
if (obj == object)
{
delete obj;
objects.erase(it);
return;
if (object != nullptr) {
size_t idx = 0;
for (ModelObject *model_object : objects) {
if (model_object == object) {
delete model_object;
objects.erase(objects.begin() + idx);
return true;
}
++ idx;
}
}
return false;
}
bool Model::delete_object(ModelID id)
{
if (id.id != 0) {
size_t idx = 0;
for (ModelObject *model_object : objects) {
if (model_object->id() == id) {
delete model_object;
objects.erase(objects.begin() + idx);
return true;
}
++ idx;
}
}
return false;
}
void Model::clear_objects()
@ -206,6 +254,7 @@ void Model::clear_materials()
ModelMaterial* Model::add_material(t_model_material_id material_id)
{
assert(! material_id.empty());
ModelMaterial* material = this->get_material(material_id);
if (material == nullptr)
material = this->materials[material_id] = new ModelMaterial(this);
@ -214,11 +263,13 @@ ModelMaterial* Model::add_material(t_model_material_id material_id)
ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other)
{
assert(! material_id.empty());
// delete existing material if any
ModelMaterial* material = this->get_material(material_id);
delete material;
// set new material
material = new ModelMaterial(this, other);
material = new ModelMaterial(other);
material->set_model(this);
this->materials[material_id] = material;
return material;
}
@ -421,6 +472,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
object->name = this->objects.front()->name;
//FIXME copy the config etc?
reset_auto_extruder_id();
@ -483,53 +535,90 @@ void Model::reset_auto_extruder_id()
s_auto_extruder_id = 1;
}
ModelObject::ModelObject(Model *model, const ModelObject &rhs, bool copy_volumes) :
m_model(model)
{
this->assign(&rhs, copy_volumes);
}
ModelObject::~ModelObject()
{
this->clear_volumes();
this->clear_instances();
}
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
ModelObject* ModelObject::clone(Model *parent)
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
{
return new ModelObject(parent, *this, true);
}
this->copy_id(rhs);
ModelObject& ModelObject::assign(const ModelObject *rhs, bool copy_volumes)
{
m_id = rhs->m_id;
name = rhs->name;
input_file = rhs->input_file;
config = rhs->config;
sla_support_points = rhs->sla_support_points;
layer_height_ranges = rhs->layer_height_ranges;
layer_height_profile = rhs->layer_height_profile;
layer_height_profile_valid = rhs->layer_height_profile_valid;
origin_translation = rhs->origin_translation;
m_bounding_box = rhs->m_bounding_box;
m_bounding_box_valid = rhs->m_bounding_box_valid;
this->name = rhs.name;
this->input_file = rhs.input_file;
this->config = rhs.config;
this->sla_support_points = rhs.sla_support_points;
this->layer_height_ranges = rhs.layer_height_ranges;
this->layer_height_profile = rhs.layer_height_profile;
this->layer_height_profile_valid = rhs.layer_height_profile_valid;
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
volumes.clear();
instances.clear();
if (copy_volumes) {
this->volumes.reserve(rhs->volumes.size());
for (ModelVolume *model_volume : rhs->volumes)
this->add_volume(*model_volume);
this->instances.reserve(rhs->instances.size());
for (const ModelInstance *model_instance : rhs->instances)
this->add_instance(*model_instance);
this->clear_volumes();
this->volumes.reserve(rhs.volumes.size());
for (ModelVolume *model_volume : rhs.volumes) {
this->volumes.emplace_back(new ModelVolume(*model_volume));
this->volumes.back()->set_model_object(this);
}
this->clear_instances();
this->instances.reserve(rhs.instances.size());
for (const ModelInstance *model_instance : rhs.instances) {
this->instances.emplace_back(new ModelInstance(*model_instance));
this->instances.back()->set_model_object(this);
}
return *this;
}
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
{
this->copy_id(rhs);
this->name = std::move(rhs.name);
this->input_file = std::move(rhs.input_file);
this->config = std::move(rhs.config);
this->sla_support_points = std::move(rhs.sla_support_points);
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->layer_height_profile_valid = std::move(rhs.layer_height_profile_valid);
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
this->clear_volumes();
this->volumes = std::move(rhs.volumes);
rhs.volumes.clear();
for (ModelVolume *model_volume : this->volumes)
model_volume->set_model_object(this);
this->clear_instances();
this->instances = std::move(rhs.instances);
rhs.instances.clear();
for (ModelInstance *model_instance : this->instances)
model_instance->set_model_object(this);
return *this;
}
void ModelObject::assign_new_unique_ids_recursive()
{
this->set_new_unique_id();
for (ModelVolume *model_volume : this->volumes)
model_volume->assign_new_unique_ids_recursive();
for (ModelInstance *model_instance : this->instances)
model_instance->assign_new_unique_ids_recursive();
}
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
//ModelObject* ModelObject::clone(Model *parent)
//{
// return new ModelObject(parent, *this, true);
//}
ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
{
ModelVolume* v = new ModelVolume(this, mesh);
@ -554,6 +643,14 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
return v;
}
ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh)
{
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
this->volumes.push_back(v);
this->invalidate_bounding_box();
return v;
}
void ModelObject::delete_volume(size_t idx)
{
ModelVolumePtrs::iterator i = this->volumes.begin() + idx;
@ -624,8 +721,16 @@ const BoundingBoxf3& ModelObject::bounding_box() const
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh m = v->mesh;
m.transform(v->get_matrix());
raw_bbox.merge(m.bounding_box());
}
#else
// mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box());
#endif // ENABLE_MODELVOLUME_TRANSFORM
BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances)
bb.merge(i->transform_bounding_box(raw_bbox));
@ -656,7 +761,15 @@ TriangleMesh ModelObject::raw_mesh() const
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
mesh.merge(v->mesh);
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh vol_mesh(v->mesh);
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
#else
mesh.merge(v->mesh);
#endif // ENABLE_MODELVOLUME_TRANSFORM
return mesh;
}
@ -699,12 +812,14 @@ void ModelObject::center_around_origin()
this->translate(shift);
this->origin_translation += shift;
#if !ENABLE_MODELVOLUME_TRANSFORM
if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) {
i->set_offset(i->get_offset() - shift);
}
this->invalidate_bounding_box();
}
#endif // !ENABLE_MODELVOLUME_TRANSFORM
}
void ModelObject::ensure_on_bed()
@ -731,8 +846,7 @@ void ModelObject::translate(double x, double y, double z)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.translate(float(x), float(y), float(z));
v->m_convex_hull.translate(float(x), float(y), float(z));
v->translate(x, y, z);
}
if (m_bounding_box_valid)
@ -743,51 +857,55 @@ void ModelObject::scale(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.scale(versor);
v->m_convex_hull.scale(versor);
v->scale(versor);
}
#if !ENABLE_MODELVOLUME_TRANSFORM
// reset origin translation since it doesn't make sense anymore
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
void ModelObject::rotate(float angle, const Axis& axis)
void ModelObject::rotate(double angle, Axis axis)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.rotate(angle, axis);
v->m_convex_hull.rotate(angle, axis);
v->rotate(angle, axis);
}
center_around_origin();
#if !ENABLE_MODELVOLUME_TRANSFORM
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
void ModelObject::rotate(float angle, const Vec3d& axis)
void ModelObject::rotate(double angle, const Vec3d& axis)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.rotate(angle, axis);
v->m_convex_hull.rotate(angle, axis);
v->rotate(angle, axis);
}
center_around_origin();
#if !ENABLE_MODELVOLUME_TRANSFORM
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
void ModelObject::mirror(const Axis &axis)
void ModelObject::mirror(Axis axis)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.mirror(axis);
v->m_convex_hull.mirror(axis);
v->mirror(axis);
}
#if !ENABLE_MODELVOLUME_TRANSFORM
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
@ -864,7 +982,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
if (this->volumes.size() > 1) {
// We can't split meshes if there's more than one volume, because
// we can't group the resulting meshes by object afterwards
new_objects->push_back(this);
new_objects->emplace_back(this);
return;
}
@ -876,16 +994,14 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
mesh->repair();
ModelObject* new_object = m_model->add_object(*this, false);
new_object->sla_support_points.clear();
new_object->input_file = "";
ModelVolume* new_volume = new_object->add_volume(*mesh);
new_volume->name = volume->name;
new_volume->config = volume->config;
new_volume->set_type(volume->type());
new_volume->set_material_id(volume->material_id());
new_objects->push_back(new_object);
ModelObject* new_object = m_model->add_object();
new_object->name = this->name;
new_object->config = this->config;
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
new_object->add_volume(*volume, std::move(*mesh));
new_objects->emplace_back(new_object);
delete mesh;
}
@ -918,17 +1034,32 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
double min_z = DBL_MAX;
ModelInstance* inst = instances[instance_idx];
const Transform3d& m = inst->get_matrix(true);
const Transform3d& mi = inst->get_matrix(true);
for (ModelVolume *v : volumes)
for (const ModelVolume* v : volumes)
{
if (!v->is_model_part())
continue;
#if ENABLE_MODELVOLUME_TRANSFORM
Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull();
for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f)
{
const stl_facet* facet = hull.stl.facet_start + f;
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast<double>()));
}
#else
for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f)
{
const stl_facet* facet = v->mesh.stl.facet_start + f;
min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[2].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast<double>()));
}
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
return min_z + inst->get_offset(Z);
@ -945,7 +1076,11 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3
unsigned int inside_outside = 0;
for (const ModelVolume *vol : this->volumes)
if (vol->is_model_part()) {
#if ENABLE_MODELVOLUME_TRANSFORM
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix());
#else
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix());
#endif // ENABLE_MODELVOLUME_TRANSFORM
if (print_volume.contains(bb))
inside_outside |= INSIDE;
else if (print_volume.intersects(bb))
@ -1007,9 +1142,9 @@ void ModelObject::print_info() const
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
// ensure m_material_id references an existing material
(void)this->object->get_model()->add_material(material_id);
if (! material_id.empty())
this->object->get_model()->add_material(material_id);
}
ModelMaterial* ModelVolume::material() const
@ -1020,17 +1155,17 @@ ModelMaterial* ModelVolume::material() const
void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material)
{
m_material_id = material_id;
(void)this->object->get_model()->add_material(material_id, material);
if (! material_id.empty())
this->object->get_model()->add_material(material_id, material);
}
ModelMaterial* ModelVolume::assign_unique_material()
#if ENABLE_MODELVOLUME_TRANSFORM
void ModelVolume::translate_geometry(const Vec3d& displacement)
{
Model* model = this->get_object()->get_model();
// as material-id "0" is reserved by the AMF spec we start from 1
m_material_id = 1 + model->materials.size(); // watchout for implicit cast
return model->add_material(m_material_id);
mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
}
#endif // ENABLE_MODELVOLUME_TRANSFORM
void ModelVolume::calculate_convex_hull()
{
@ -1108,15 +1243,66 @@ size_t ModelVolume::split(unsigned int max_extruders)
return idx;
}
void ModelVolume::translate(double x, double y, double z)
{
translate(Vec3d(x, y, z));
}
void ModelVolume::translate(const Vec3d& displacement)
{
#if ENABLE_MODELVOLUME_TRANSFORM
set_offset(get_offset() + displacement);
#else
mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::scale(const Vec3d& scaling_factors)
{
#if ENABLE_MODELVOLUME_TRANSFORM
set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors));
#else
mesh.scale(scaling_factors);
m_convex_hull.scale(scaling_factors);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::rotate(double angle, Axis axis)
{
#if ENABLE_MODELVOLUME_TRANSFORM
switch (axis)
{
case X: { rotate(angle, Vec3d::UnitX()); break; }
case Y: { rotate(angle, Vec3d::UnitY()); break; }
case Z: { rotate(angle, Vec3d::UnitZ()); break; }
}
#else
mesh.rotate(angle, axis);
m_convex_hull.rotate(angle, axis);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::rotate(double angle, const Vec3d& axis)
{
#if ENABLE_MODELVOLUME_TRANSFORM
set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix()));
#else
mesh.rotate(angle, axis);
m_convex_hull.rotate(angle, axis);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::mirror(Axis axis)
{
#if ENABLE_MODELVOLUME_TRANSFORM
Vec3d mirror = get_mirror();
switch (axis)
{
case X: { mirror(0) *= -1.0; break; }
case Y: { mirror(1) *= -1.0; break; }
case Z: { mirror(2) *= -1.0; break; }
}
set_mirror(mirror);
#else
mesh.mirror(axis);
m_convex_hull.mirror(axis);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
#if !ENABLE_MODELVOLUME_TRANSFORM
@ -1174,14 +1360,14 @@ void ModelInstance::set_mirror(Axis axis, double mirror)
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
mesh->transform(get_matrix(dont_translate).cast<float>());
mesh->transform(get_matrix(dont_translate));
}
BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const
{
// Rotate around mesh origin.
TriangleMesh copy(*mesh);
copy.transform(get_matrix(true, false, true, true).cast<float>());
copy.transform(get_matrix(true, false, true, true));
BoundingBoxf3 bbox = copy.bounding_box();
if (!empty(bbox)) {
@ -1189,10 +1375,10 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
for (unsigned int i = 0; i < 3; ++i)
{
#if ENABLE_MODELVOLUME_TRANSFORM
if (std::abs(m_transformation.get_scaling_factor((Axis)i)-1.0) > EPSILON)
if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON)
{
bbox.min(i) *= m_transformation.get_scaling_factor((Axis)i);
bbox.max(i) *= m_transformation.get_scaling_factor((Axis)i);
bbox.min(i) *= get_scaling_factor((Axis)i);
bbox.max(i) *= get_scaling_factor((Axis)i);
#else
if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON)
{
@ -1205,8 +1391,8 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
// Translate the bounding box.
if (! dont_translate) {
#if ENABLE_MODELVOLUME_TRANSFORM
bbox.min += m_transformation.get_offset();
bbox.max += m_transformation.get_offset();
bbox.min += get_offset();
bbox.max += get_offset();
#else
bbox.min += this->m_offset;
bbox.max += this->m_offset;
@ -1230,9 +1416,9 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
{
#if ENABLE_MODELVOLUME_TRANSFORM
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
polygon->rotate(m_transformation.get_rotation(Z)); // rotate around polygon origin
polygon->rotate(get_rotation(Z)); // rotate around polygon origin
// CHECK_ME -> Is the following correct ?
polygon->scale(m_transformation.get_scaling_factor(X), m_transformation.get_scaling_factor(Y)); // scale around polygon origin
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
#else
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
polygon->rotate(this->m_rotation(2)); // rotate around polygon origin

View file

@ -22,21 +22,35 @@ class ModelInstance;
class ModelMaterial;
class ModelObject;
class ModelVolume;
class PresetBundle;
class Print;
typedef std::string t_model_material_id;
typedef std::string t_model_material_attribute;
typedef std::map<t_model_material_attribute,std::string> t_model_material_attributes;
typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes;
typedef std::map<t_model_material_id,ModelMaterial*> ModelMaterialMap;
typedef std::map<t_model_material_id, ModelMaterial*> ModelMaterialMap;
typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs;
// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
typedef size_t ModelID;
// Valid IDs are strictly positive (non zero).
// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
// for parameter overload.
struct ModelID
{
ModelID(size_t id) : id(id) {}
bool operator==(const ModelID &rhs) const { return this->id == rhs.id; }
bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; }
bool operator< (const ModelID &rhs) const { return this->id < rhs.id; }
bool operator> (const ModelID &rhs) const { return this->id > rhs.id; }
bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; }
bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; }
size_t id;
};
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
@ -45,22 +59,69 @@ typedef size_t ModelID;
class ModelBase
{
public:
ModelID id() const { return m_id; }
ModelID id() const { return m_id; }
protected:
// Constructor to be only called by derived classes.
ModelBase() {}
ModelID m_id = generate_new_id();
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
ModelBase() : m_id(generate_new_id()) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
ModelBase(int) : m_id(ModelID(0)) {}
// Use with caution!
void set_new_unique_id() { m_id = generate_new_id(); }
void set_invalid_id() { m_id = 0; }
// Use with caution!
void copy_id(const ModelBase &rhs) { m_id = rhs.id(); }
// Override this method if a ModelBase derived class owns other ModelBase derived instances.
void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
private:
static inline ModelID generate_new_id() { return s_last_id ++; }
static ModelID s_last_id;
ModelID m_id;
static inline ModelID generate_new_id() { return ModelID(++ s_last_id); }
static size_t s_last_id;
};
#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
/* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \
/* to make a private copy for background processing. */ \
static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \
static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \
static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \
static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \
TYPE& assign_copy(const TYPE &rhs); \
TYPE& assign_copy(TYPE &&rhs); \
/* Copy a TYPE, generate new IDs. The front end will use this call. */ \
static TYPE* new_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
auto obj = new TYPE(-1); \
obj->assign_clone(rhs); \
return obj; \
} \
TYPE make_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
TYPE obj(-1); \
obj.assign_clone(rhs); \
return obj; \
} \
TYPE& assign_clone(const TYPE &rhs) { \
this->assign_copy(rhs); \
this->assign_new_unique_ids_recursive(); \
return *this; \
}
#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \
private: \
/* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \
explicit TYPE(int) : ModelBase(-1) {}; \
void assign_new_unique_ids_recursive();
// Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial : public ModelBase
{
friend class Model;
public:
// Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
t_model_material_attributes attributes;
@ -71,14 +132,22 @@ public:
void apply(const t_model_material_attributes &attributes)
{ this->attributes.insert(attributes.begin(), attributes.end()); }
protected:
friend class Model;
// Constructor, which assigns a new unique ID.
ModelMaterial(Model *model) : m_model(model) {}
// Copy constructor copies the ID and m_model!
ModelMaterial(const ModelMaterial &rhs) = default;
void set_model(Model *model) { m_model = model; }
private:
// Parent, owning this material.
Model *m_model;
ModelMaterial(Model *model) : m_model(model) {}
ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), m_model(model) {}
explicit ModelMaterial(ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &rhs) = delete;
ModelMaterial() = delete;
ModelMaterial(ModelMaterial &&rhs) = delete;
ModelMaterial& operator=(const ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &&rhs) = delete;
};
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
@ -119,15 +188,12 @@ public:
when user expects that. */
Vec3d origin_translation;
// Assign a ModelObject to this object while keeping the original pointer to the parent Model.
// Make a deep copy.
ModelObject& assign(const ModelObject *rhs, bool copy_volumes = true);
Model* get_model() const { return m_model; };
ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh);
ModelVolume* add_volume(const ModelVolume &volume);
ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh);
void delete_volume(size_t idx);
void clear_volumes();
bool is_multiparts() const { return volumes.size() > 1; }
@ -163,9 +229,10 @@ public:
void translate(double x, double y, double z);
void scale(const Vec3d &versor);
void scale(const double s) { this->scale(Vec3d(s, s, s)); }
void rotate(float angle, const Axis &axis);
void rotate(float angle, const Vec3d& axis);
void mirror(const Axis &axis);
void scale(double x, double y, double z) { this->scale(Vec3d(x, y, z)); }
void rotate(double angle, Axis axis);
void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis);
size_t materials_count() const;
size_t facets_count() const;
bool needed_repair() const;
@ -184,22 +251,27 @@ public:
protected:
friend class Print;
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
ModelObject* clone(Model *parent);
void set_model(Model *model) { m_model = model; }
// Called by Print::apply() to set the model pointer after making a copy.
void set_model(Model *model) { m_model = model; }
private:
ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
ModelObject(Model *model, const ModelObject &rhs, bool copy_volumes = true);
explicit ModelObject(ModelObject &rhs) = delete;
~ModelObject();
ModelObject& operator=(ModelObject &rhs) = default;
// Parent object, owning this ModelObject.
Model *m_model;
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); }
explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; }
ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; }
MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject)
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
Model *m_model = nullptr;
// Bounding box, cached.
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
};
@ -208,8 +280,6 @@ private:
// ModelVolume instances are owned by a ModelObject.
class ModelVolume : public ModelBase
{
friend class ModelObject;
public:
std::string name;
// The triangular model.
@ -226,9 +296,6 @@ public:
SUPPORT_BLOCKER,
};
// Clone this ModelVolume, keep the ID identical, set the parent to the cloned volume.
ModelVolume* clone(ModelObject *parent) { return new ModelVolume(parent, *this); }
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
Type type() const { return m_type; }
@ -246,11 +313,19 @@ public:
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
size_t split(unsigned int max_extruders);
void translate(double x, double y, double z);
void translate(double x, double y, double z) { translate(Vec3d(x, y, z)); }
void translate(const Vec3d& displacement);
void scale(const Vec3d& scaling_factors);
void scale(double x, double y, double z) { scale(Vec3d(x, y, z)); }
void scale(double s) { scale(Vec3d(s, s, s)); }
void rotate(double angle, Axis axis);
void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis);
#if ENABLE_MODELVOLUME_TRANSFORM
void translate_geometry(const Vec3d& displacement);
#endif // ENABLE_MODELVOLUME_TRANSFORM
ModelMaterial* assign_unique_material();
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
@ -289,6 +364,13 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
#endif // ENABLE_MODELVOLUME_TRANSFORM
protected:
friend class Print;
friend class ModelObject;
explicit ModelVolume(ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
private:
// Parent object owning this ModelVolume.
ModelObject* object;
@ -306,24 +388,45 @@ private:
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
#if ENABLE_MODELVOLUME_TRANSFORM
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
ModelBase(other), // copy the ID
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
#else
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
{
if (! other.material_id().empty())
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
{
if (! other.material_id().empty())
this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
#endif // ENABLE_MODELVOLUME_TRANSFORM
explicit ModelVolume(ModelVolume &rhs) = delete;
ModelVolume& operator=(ModelVolume &rhs) = delete;
};
@ -340,8 +443,6 @@ public:
Num_BedStates
};
friend class ModelObject;
private:
#if ENABLE_MODELVOLUME_TRANSFORM
Geometry::Transformation m_transformation;
@ -430,22 +531,33 @@ public:
bool is_printable() const { return print_volume_state == PVS_Inside; }
protected:
friend class Print;
friend class ModelObject;
explicit ModelInstance(const ModelInstance &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
private:
// Parent object, owning this instance.
ModelObject* object;
#if ENABLE_MODELVOLUME_TRANSFORM
ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {}
#else
ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
explicit ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {}
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {}
#endif // ENABLE_MODELVOLUME_TRANSFORM
explicit ModelInstance(ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &rhs) = delete;
ModelInstance() = delete;
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &&rhs) = delete;
};
// The print bed content.
@ -460,30 +572,39 @@ class Model : public ModelBase
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
ModelMaterialMap materials;
ModelMaterialMap materials;
// Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
ModelObjectPtrs objects;
ModelObjectPtrs objects;
// Default constructor assigns a new ID to the model.
Model() {}
Model(const Model &rhs);
Model& operator=(const Model &rhs);
~Model() { this->clear_objects(); this->clear_materials(); }
// XXX: use fs::path ?
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); }
explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; }
Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; }
MODELBASE_DERIVED_COPY_MOVE_CLONE(Model)
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair();
void repair();
// Add a new ModelObject to this Model, generate a new ID for this ModelObject.
ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
void delete_object(size_t idx);
void delete_object(ModelObject* object);
void clear_objects();
ModelObject* add_object(const ModelObject &other);
void delete_object(size_t idx);
bool delete_object(ModelID id);
bool delete_object(ModelObject* object);
void clear_objects();
ModelMaterial* add_material(t_model_material_id material_id);
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
@ -492,9 +613,9 @@ public:
return (i == this->materials.end()) ? nullptr : i->second;
}
void delete_material(t_model_material_id material_id);
void clear_materials();
bool add_default_instances();
void delete_material(t_model_material_id material_id);
void clear_materials();
bool add_default_instances();
// Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances,
@ -520,8 +641,14 @@ public:
static unsigned int get_auto_extruder_id(unsigned int max_extruders);
static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
static void reset_auto_extruder_id();
private:
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)
};
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
}
#endif

View file

@ -58,7 +58,7 @@ inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }

View file

@ -124,6 +124,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"between_objects_gcode",
"bridge_acceleration",
"bridge_fan_speed",
"colorprint_heights",
"cooling",
"default_acceleration",
"deretract_speed",
@ -603,7 +604,7 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old
size_t i_old, i_new;
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
const ModelVolume &mv_new = *model_object_new.volumes[i_old];
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
if (mv_old.type() != type) {
++ i_old;
continue;
@ -615,8 +616,12 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old
if (mv_old.id() != mv_new.id())
return true;
//FIXME test for the content of the mesh!
//FIXME test for the transformation matrices!
++ i_old;
#if ENABLE_MODELVOLUME_TRANSFORM
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
return true;
#endif // ENABLE_MODELVOLUME_TRANSFORM
++i_old;
++ i_new;
}
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
@ -634,7 +639,7 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old
return false;
}
static inline void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src)
void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src)
{
// 1) Delete the support volumes from model_object_dst.
{
@ -650,8 +655,10 @@ static inline void model_volume_list_update_supports(ModelObject &model_object_d
}
// 2) Copy the support volumes from model_object_src to the end of model_object_dst.
for (ModelVolume *vol : model_object_src.volumes) {
if (vol->is_support_modifier())
model_object_dst.volumes.emplace_back(vol->clone(&model_object_dst));
if (vol->is_support_modifier()) {
model_object_dst.volumes.emplace_back(new ModelVolume(*vol));
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
}
}
}
@ -709,8 +716,60 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
}
#ifdef _DEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
static inline void check_model_ids_validity(const Model &model)
{
std::set<ModelID> ids;
auto check = [&ids](ModelID id) {
assert(id.id > 0);
assert(ids.find(id) == ids.end());
ids.insert(id);
};
for (const ModelObject *model_object : model.objects) {
check(model_object->id());
for (const ModelVolume *model_volume : model_object->volumes)
check(model_volume->id());
for (const ModelInstance *model_instance : model_object->instances)
check(model_instance->id());
}
for (const auto mm : model.materials)
check(mm.second->id());
}
static inline void check_model_ids_equal(const Model &model1, const Model &model2)
{
// Verify whether the IDs of model1 and model match.
assert(model1.objects.size() == model2.objects.size());
for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) {
const ModelObject &model_object1 = *model1.objects[idx_model];
const ModelObject &model_object2 = * model2.objects[idx_model];
assert(model_object1.id() == model_object2.id());
assert(model_object1.volumes.size() == model_object2.volumes.size());
assert(model_object1.instances.size() == model_object2.instances.size());
for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
}
assert(model1.materials.size() == model2.materials.size());
{
auto it1 = model1.materials.begin();
auto it2 = model2.materials.begin();
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
assert(it1->first == it2->first); // compare keys
assert(it1->second->id() == it2->second->id());
}
}
}
#endif /* _DEBUG */
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
// Make a copy of the config, normalize it.
DynamicPrintConfig config(config_in);
config.normalize();
@ -774,7 +833,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
m_model = model;
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
@ -789,7 +848,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
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(model.objects[i]->clone(&m_model));
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.
@ -806,7 +866,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
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((*it)->clone(&m_model));
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).
@ -899,8 +960,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
update_apply_status(it->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, reset the parent.
model_object.assign(&model_object_new);
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (support_blockers_differ || support_enforcers_differ) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
m_cancel_callback();
@ -933,8 +994,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
for (const ModelInstance *model_instance : model_object_new.instances)
model_object.add_instance(*model_instance);
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);
}
}
}
@ -1136,6 +1200,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
object->update_layer_height_profile();
this->update_object_placeholders();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
return static_cast<ApplyStatus>(apply_status);
}

View file

@ -564,6 +564,9 @@ private:
void _make_wipe_tower();
void _simplify_slices(double distance);
// Declared here to have access to Model / ModelObject / ModelInstance
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
PrintState<PrintStep, psCount> m_state;
// Mutex used for synchronization of the worker thread with the UI thread:
// The mutex will be used to guard the worker thread against entering a stage
@ -601,12 +604,6 @@ private:
friend class PrintObject;
};
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->m_layers, layer)
#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->m_regions, layerm)
}
#endif

View file

@ -192,6 +192,12 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->default_value = new ConfigOptionBool(false);
def = this->add("colorprint_heights", coFloats);
def->label = L("Colorprint height");
def->tooltip = L("Heights at which a filament change is to occur. ");
def->cli = "colorprint-heights=f@";
def->default_value = new ConfigOptionFloats { };
def = this->add("compatible_printers", coStrings);
def->label = L("Compatible printers");
def->mode = comAdvanced;

View file

@ -704,6 +704,7 @@ public:
ConfigOptionInts bridge_fan_speed;
ConfigOptionFloat brim_width;
ConfigOptionBool complete_objects;
ConfigOptionFloats colorprint_heights;
ConfigOptionBools cooling;
ConfigOptionFloat default_acceleration;
ConfigOptionInts disable_fan_first_layers;
@ -781,6 +782,7 @@ protected:
OPT_PTR(bridge_fan_speed);
OPT_PTR(brim_width);
OPT_PTR(complete_objects);
OPT_PTR(colorprint_heights);
OPT_PTR(cooling);
OPT_PTR(default_acceleration);
OPT_PTR(disable_fan_first_layers);

View file

@ -170,8 +170,8 @@ void PrintObject::make_perimeters()
// merge slices if they were split into types
if (this->typed_slices) {
FOREACH_LAYER(this, layer_it) {
(*layer_it)->merge_slices();
for (Layer *layer : m_layers) {
layer->merge_slices();
m_print->throw_if_canceled();
}
this->typed_slices = false;
@ -184,8 +184,8 @@ void PrintObject::make_perimeters()
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
@ -313,7 +313,7 @@ void PrintObject::prepare_infill()
// Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
@ -332,7 +332,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
@ -351,7 +351,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
@ -370,7 +370,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
@ -633,7 +633,7 @@ void PrintObject::detect_surfaces_type()
// should be visible.
bool interface_shells = m_config.interface_shells.value;
for (int idx_region = 0; idx_region < m_print->m_regions.size(); ++ idx_region) {
for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (Layer *layer : m_layers)
@ -818,7 +818,7 @@ void PrintObject::process_external_surfaces()
{
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
@ -851,13 +851,13 @@ void PrintObject::discover_vertical_shells()
Polygons holes;
};
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = m_print->regions().size() > 1 && ! m_config.interface_shells.value;
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
if (top_bottom_surfaces_all_regions) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false;
for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) {
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
const PrintRegion &region = *m_print->get_region(idx_region);
if (region.config().ensure_vertical_shell_thickness.value &&
(region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) {
@ -874,7 +874,7 @@ void PrintObject::discover_vertical_shells()
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
const size_t num_regions = m_print->regions().size();
const size_t num_regions = this->region_volumes.size();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
const Layer &layer = *m_layers[idx_layer];
@ -935,7 +935,7 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
}
for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) {
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
PROFILE_BLOCK(discover_vertical_shells_region);
const PrintRegion &region = *m_print->get_region(idx_region);
@ -1227,7 +1227,7 @@ void PrintObject::bridge_over_infill()
{
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
// skip bridging in case there are no voids
@ -1243,9 +1243,10 @@ void PrintObject::bridge_over_infill()
*this
);
FOREACH_LAYER(this, layer_it) {
for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) {
// skip first layer
if (layer_it == m_layers.begin()) continue;
if (layer_it == m_layers.begin())
continue;
Layer* layer = *layer_it;
LayerRegion* layerm = layer->m_regions[region_id];
@ -1271,8 +1272,8 @@ void PrintObject::bridge_over_infill()
// iterate through regions and collect internal surfaces
Polygons lower_internal;
FOREACH_LAYERREGION(lower_layer, lower_layerm_it)
(*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal);
for (LayerRegion *lower_layerm : lower_layer->m_regions)
lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal);
// intersect such lower internal surfaces with the candidate solid surfaces
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
@ -1443,14 +1444,14 @@ void PrintObject::_slice()
layer->lower_layer = prev;
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id)
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
layer->add_region(this->print()->regions()[region_id]);
prev = layer;
}
}
// Slice all non-modifier volumes.
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
m_print->throw_if_canceled();
@ -1462,14 +1463,14 @@ void PrintObject::_slice()
}
// Slice all modifier volumes.
if (this->print()->regions().size() > 1) {
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
if (this->region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
m_print->throw_if_canceled();
// loop through the other regions and 'steal' the slices belonging to this one
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
for (size_t other_region_id = 0; other_region_id < this->print()->regions().size(); ++ other_region_id) {
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
@ -1496,7 +1497,7 @@ void PrintObject::_slice()
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers";
while (! m_layers.empty()) {
const Layer *layer = m_layers.back();
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id)
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty())
// Non empty layer.
goto end;
@ -1601,9 +1602,17 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh;
for (const ModelVolume *v : volumes)
mesh.merge(v->mesh);
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh vol_mesh(v->mesh);
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
#else
mesh.merge(v->mesh);
#endif // ENABLE_MODELVOLUME_TRANSFORM
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo.cast<float>());
mesh.transform(m_trafo);
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
@ -1734,8 +1743,8 @@ void PrintObject::_make_perimeters()
// merge slices if they were split into types
if (this->typed_slices) {
FOREACH_LAYER(this, layer_it)
(*layer_it)->merge_slices();
for (Layer *layer : m_layers)
layer->merge_slices();
this->typed_slices = false;
this->invalidate_step(posPrepareInfill);
}
@ -1747,7 +1756,7 @@ void PrintObject::_make_perimeters()
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
@ -1919,7 +1928,7 @@ void PrintObject::discover_horizontal_shells()
{
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (int i = 0; i < int(m_layers.size()); ++ i) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[i]->regions()[region_id];
@ -2093,7 +2102,7 @@ void PrintObject::discover_horizontal_shells()
} // for each region
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
const LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
@ -2109,7 +2118,7 @@ void PrintObject::discover_horizontal_shells()
void PrintObject::combine_infill()
{
// Work on each region separately.
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions()[region_id];
const int every = region->config().infill_every_layers.value;
if (every < 2 || region->config().fill_density == 0.)

View file

@ -2450,7 +2450,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
// Transform loops into ExtrusionPath objects.
extrusion_entities_append_paths(
top_contact_layer.extrusions,
STDMOVE(loop_lines),
std::move(loop_lines),
erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height);
}
@ -2827,7 +2827,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing()));
extrusion_entities_append_paths(
support_layer.support_fills.entities,
to_polylines(STDMOVE(to_infill_polygons)),
to_polylines(std::move(to_infill_polygons)),
erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height);
}
if (! to_infill.empty()) {
@ -2841,7 +2841,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
STDMOVE(to_infill),
std::move(to_infill),
// Filler and its parameters
filler, float(support_density),
// Extrusion parameters
@ -3037,14 +3037,14 @@ void PrintObjectSupportMaterial::generate_toolpaths(
to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing()));
extrusion_entities_append_paths(
base_layer.extrusions,
to_polylines(STDMOVE(to_infill_polygons)),
to_polylines(std::move(to_infill_polygons)),
erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height);
}
fill_expolygons_generate_paths(
// Destination
base_layer.extrusions,
// Regions to fill
STDMOVE(to_infill),
std::move(to_infill),
// Filler and its parameters
filler, density,
// Extrusion parameters

View file

@ -18,7 +18,7 @@
// Uses a unique opengl context
#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0)
// Disable synchronization of unselected instances
#define DISABLE_INSTANCES_SYNCH (1 && ENABLE_1_42_0)
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0)
// Modified camera target behavior
#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0)
// Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume

View file

@ -272,9 +272,9 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
if (angle == 0.f)
return;
Vec3f axis_norm = axis.cast<float>().normalized();
Transform3f m = Transform3f::Identity();
m.rotate(Eigen::AngleAxisf(angle, axis_norm));
Vec3d axis_norm = axis.normalized();
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(angle, axis_norm));
stl_transform(&stl, m);
}
@ -290,7 +290,7 @@ void TriangleMesh::mirror(const Axis &axis)
stl_invalidate_shared_vertices(&this->stl);
}
void TriangleMesh::transform(const Transform3f& t)
void TriangleMesh::transform(const Transform3d& t)
{
stl_transform(&stl, t);
}

View file

@ -49,7 +49,7 @@ public:
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3f& t);
void transform(const Transform3d& t);
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;

View file

@ -47,19 +47,6 @@ typedef double coordf_t;
#define scale_(val) ((val) / SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON)
// Which C++ version is supported?
// For example, could optimized functions with move semantics be used?
#if __cplusplus==201402L
#define SLIC3R_CPPVER 14
#define STDMOVE(WHAT) std::move(WHAT)
#elif __cplusplus==201103L
#define SLIC3R_CPPVER 11
#define STDMOVE(WHAT) std::move(WHAT)
#else
#define SLIC3R_CPPVER 0
#define STDMOVE(WHAT) (WHAT)
#endif
#define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/"
inline std::string debug_out_path(const char *name, ...)