diff --git a/resources/icons/support_blocker_.png b/resources/icons/support_blocker_.png new file mode 100644 index 000000000..0873c5e6e Binary files /dev/null and b/resources/icons/support_blocker_.png differ diff --git a/resources/icons/support_enforcer_.png b/resources/icons/support_enforcer_.png new file mode 100644 index 000000000..265cebcb9 Binary files /dev/null and b/resources/icons/support_enforcer_.png differ diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 9898f3e64..9c71f00f6 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -173,7 +173,7 @@ extern void stl_mirror_xy(stl_file *stl); extern void stl_mirror_yz(stl_file *stl); extern void stl_mirror_xz(stl_file *stl); extern void stl_transform(stl_file *stl, float *trafo3x4); -extern void stl_transform(stl_file *stl, const Eigen::Transform& t); +extern void stl_transform(stl_file *stl, const Eigen::Transform& t); extern void stl_open_merge(stl_file *stl, char *file); extern void stl_invalidate_shared_vertices(stl_file *stl); extern void stl_generate_shared_vertices(stl_file *stl); @@ -189,7 +189,7 @@ inline void stl_normalize_vector(stl_normal &normal) { if (length < 0.000000000001) normal = stl_normal::Zero(); else - normal *= (1.0 / length); + normal *= float(1.0 / length); } inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) { return (a(0) != b(0)) ? (a(0) < b(0)) : diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index cc104fdd1..7cb69bccd 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -155,7 +155,7 @@ void stl_transform(stl_file *stl, float *trafo3x4) { calculate_normals(stl); } -void stl_transform(stl_file *stl, const Eigen::Transform& t) +void stl_transform(stl_file *stl, const Eigen::Transform& t) { if (stl->error) return; @@ -178,7 +178,7 @@ void stl_transform(stl_file *stl, const Eigen::Transform() * src_vertices.colwise().homogeneous(); facet_ptr = stl->facet_start; v_id = 0; diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index bf8be7feb..bfa0991a7 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -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(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 diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index fb42a85ae..2d8d049ba 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -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. diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index f1436c931..016d162d8 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -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() ? diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index d3b28be26..fc2cf7b89 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -813,7 +813,7 @@ namespace Slic3r { std::vector sla_support_points; for (unsigned int i=0; i()); 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(); diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index cfab1c600..44b37c1ae 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -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 diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 36964d869..089368865 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -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 << "Slic3r " << SLIC3R_VERSION << "\n"; stream << "" << VERSION_AMF << "\n"; - if (export_print_config) + if (config != nullptr) { - std::string config = "\n"; - GCode::append_full_config(*print, config); - stream << "" << xml_escape(config) << "\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 << "" << xml_escape(str_config) << "\n"; } for (const auto &material : model->materials) { diff --git a/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp index ae8863e02..e085ad22e 100644 --- a/src/libslic3r/Format/AMF.hpp +++ b/src/libslic3r/Format/AMF.hpp @@ -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 diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 52bdf1d1b..b6752147b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -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(print.config().colorprint_heights.values); + // Initialize autospeed. { // get the minimum cross-section used in the print std::vector 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 ®ion = *print.regions()[region_id]; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 45f17a68a..bf65311db 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -317,6 +317,9 @@ protected: bool m_second_layer_things_done; // Index of a last object copy extruded. std::pair m_last_obj_copy; + // Layer heights for colorprint - updated before the export and erased during the process + // so no toolchange occurs twice. + std::vector m_colorprint_heights; // Time estimators GCodeTimeEstimator m_normal_time_estimator; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 175b69447..3793051f4 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -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) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index afdde1852..7878bffab 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -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 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()); } } } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index e0f97703c..6f70fba65 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -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_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"); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d33f44c35..20bf9e367 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -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 &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 &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 &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())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast())); + } +#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())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[1].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[2].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast())); + min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast())); } +#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()); + 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()); + 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 diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b72f20501..b965f09ba 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -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_attributes; +typedef std::map t_model_material_attributes; -typedef std::map ModelMaterialMap; +typedef std::map ModelMaterialMap; typedef std::vector ModelObjectPtrs; typedef std::vector ModelVolumePtrs; typedef std::vector 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 diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 100178108..277fc5877 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -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(x), unscale(y)); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 16b3ff773..41e5f067d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -124,6 +124,7 @@ bool Print::invalidate_state_by_config_options(const std::vectoris_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 print_objects_from_model_object(const ModelOb return std::vector(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 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(*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(apply_status); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 3c2a87c74..705c41458 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -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 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 diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4e1de0991..374ae7d34 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d92b3758d..87a882c64 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -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); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8f9146766..45b2689bb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -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 ®ion = *m_print->regions()[region_id]; + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + const PrintRegion ®ion = *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 ®ion = *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 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 ®ion = *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(0, m_layers.size(), grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& 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 ®ion = *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 ®ion = *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_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_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 PrintObject::_slice_volumes(const std::vector &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()); + mesh.transform(m_trafo); // apply XY shift mesh.translate(- unscale(m_copies_shift(0)), - unscale(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 ®ion = *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.) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index bd1a9f3fb..31305f332 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -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 diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 819366146..31825d346 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -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 diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index c8a6e2130..425967f9f 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -272,9 +272,9 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis) if (angle == 0.f) return; - Vec3f axis_norm = axis.cast().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); } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index f6e0baea9..fd312d0e0 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -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; diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index a2ee3fc9f..f8088faea 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -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, ...) diff --git a/src/slic3r.cpp b/src/slic3r.cpp index cf86c40ed..441a6ab80 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -186,7 +186,7 @@ int main(int argc, char **argv) else // Remove the previous extension and add .3mf extention. outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf"; - store_3mf(outfile.c_str(), &model, nullptr, false); + store_3mf(outfile.c_str(), &model, nullptr); boost::nowide::cout << "File file exported to " << outfile << std::endl; } else if (cli_config.cut > 0) { model.repair(); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6fb9e4bcb..565ef94fb 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -193,6 +193,7 @@ const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; +const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) #if ENABLE_MODELVOLUME_TRANSFORM @@ -211,6 +212,7 @@ GLVolume::GLVolume(float r, float g, float b, float a) , composite_id(-1) , extruder_id(0) , selected(false) + , disabled(false) , is_active(true) , zoom_to_volumes(true) , shader_outside_printer_detection_enabled(false) @@ -252,6 +254,8 @@ void GLVolume::set_render_color() set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); else if (hover) set_render_color(HOVER_COLOR, 4); + else if (disabled) + set_render_color(DISABLED_COLOR, 4); else if (is_outside && shader_outside_printer_detection_enabled) set_render_color(OUTSIDE_COLOR, 4); else @@ -723,7 +727,11 @@ std::vector GLVolumeCollection::load_object( for (int instance_idx : instance_idxs) { const ModelInstance *instance = model_object->instances[instance_idx]; +#if ENABLE_MODELVOLUME_TRANSFORM + const TriangleMesh& mesh = model_volume->mesh; +#else TriangleMesh mesh = model_volume->mesh; +#endif // ENABLE_MODELVOLUME_TRANSFORM volumes_idx.push_back(int(this->volumes.size())); float color[4]; memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); @@ -758,7 +766,8 @@ std::vector GLVolumeCollection::load_object( v.is_modifier = ! model_volume->is_model_part(); v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); #if ENABLE_MODELVOLUME_TRANSFORM - v.set_transformation(instance->get_transformation()); + v.set_instance_transformation(instance->get_transformation()); + v.set_volume_transformation(model_volume->get_transformation()); #else v.set_offset(instance->get_offset()); v.set_rotation(instance->get_rotation()); @@ -833,7 +842,11 @@ int GLVolumeCollection::load_wipe_tower_preview( else v.indexed_vertex_array.load_mesh_flat_shading(mesh); +#if ENABLE_MODELVOLUME_TRANSFORM + v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); +#else v.set_offset(Vec3d(pos_x, pos_y, 0.0)); +#endif // ENABLE_MODELVOLUME_TRANSFORM // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index a69147a86..87c9df385 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -249,13 +249,15 @@ public: static const float HOVER_COLOR[4]; static const float OUTSIDE_COLOR[4]; static const float SELECTED_OUTSIDE_COLOR[4]; + static const float DISABLED_COLOR[4]; GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} private: #if ENABLE_MODELVOLUME_TRANSFORM - Geometry::Transformation m_transformation; + Geometry::Transformation m_instance_transformation; + Geometry::Transformation m_volume_transformation; #else // Offset of the volume to be rendered. Vec3d m_offset; @@ -294,6 +296,8 @@ public: int extruder_id; // Is this object selected? bool selected; + // Is this object disabled from selection? + bool disabled; // Whether or not this volume is active for rendering bool is_active; // Whether or not to use this volume when applying zoom_to_volumes() @@ -329,32 +333,59 @@ public: void set_render_color(); #if ENABLE_MODELVOLUME_TRANSFORM - const Geometry::Transformation& get_transformation() const { return m_transformation; } - void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; set_bounding_boxes_as_dirty(); } + const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } + void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } - const Vec3d& get_offset() const { return m_transformation.get_offset(); } - double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } + const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); } + double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); } - void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } - void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } + void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } + void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } - const Vec3d& get_rotation() const { return m_transformation.get_rotation(); } - double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } + const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); } + double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); } - void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } - void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } + void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } + void set_instance_rotation(Axis axis, double rotation) { m_instance_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } - Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); } - double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } + Vec3d get_instance_scaling_factor() const { return m_instance_transformation.get_scaling_factor(); } + double get_instance_scaling_factor(Axis axis) const { return m_instance_transformation.get_scaling_factor(axis); } - void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } - void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } + void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } + void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } - const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } - double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } + const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); } + double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); } - void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } - void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } + void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } + void set_instance_mirror(Axis axis, double mirror) { m_instance_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } + + const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; } + void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); } + + const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); } + double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); } + + void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); } + void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); } + + const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); } + double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); } + + void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); } + void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); } + + Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); } + double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); } + + void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); } + void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); } + + const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); } + double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); } + + void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } + void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } #else const Vec3d& get_rotation() const; void set_rotation(const Vec3d& rotation); @@ -378,7 +409,7 @@ public: int instance_idx() const { return this->composite_id % 1000; } #if ENABLE_MODELVOLUME_TRANSFORM - const Transform3d& world_matrix() const { return m_transformation.get_matrix(); } + Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); } #else const Transform3f& world_matrix() const; #endif // ENABLE_MODELVOLUME_TRANSFORM diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index f2b07c51e..cd438ebe5 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -1,6 +1,7 @@ #include "BackgroundSlicingProcess.hpp" #include "GUI_App.hpp" +#include #include #include #include @@ -103,6 +104,15 @@ void BackgroundSlicingProcess::thread_proc() // End of the background processing thread. The UI thread should join m_thread now. } +void BackgroundSlicingProcess::thread_proc_safe() +{ + try { + this->thread_proc(); + } catch (...) { + wxTheApp->OnUnhandledException(); + } +} + void BackgroundSlicingProcess::join_background_thread() { std::unique_lock lck(m_mutex); @@ -127,7 +137,7 @@ bool BackgroundSlicingProcess::start() if (m_state == STATE_INITIAL) { // The worker thread is not running yet. Start it. assert(! m_thread.joinable()); - m_thread = std::thread([this]{this->thread_proc();}); + m_thread = std::thread([this]{this->thread_proc_safe();}); // Wait until the worker thread is ready to execute the background processing task. m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index f00668299..6b92e8516 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -80,6 +80,7 @@ public: private: void thread_proc(); + void thread_proc_safe(); void join_background_thread(); // To be called by Print::apply() through the Print::m_cancel_callback to stop the background // processing before changing any data of running or finalized milestones. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fe1376cf2..e153161d4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1126,6 +1126,31 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } +#if ENABLE_MODELVOLUME_TRANSFORM +GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache() + : position(Vec3d::Zero()) + , rotation(Vec3d::Zero()) + , scaling_factor(Vec3d::Ones()) + , rotation_matrix(Transform3d::Identity()) + , scale_matrix(Transform3d::Identity()) +{ +} + +GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache(const Geometry::Transformation& transform) + : position(transform.get_offset()) + , rotation(transform.get_rotation()) + , scaling_factor(transform.get_scaling_factor()) +{ + rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation); + scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scaling_factor); +} + +GLCanvas3D::Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform) + : m_volume(volume_transform) + , m_instance(instance_transform) +{ +} +#else GLCanvas3D::Selection::VolumeCache::VolumeCache() : m_position(Vec3d::Zero()) , m_rotation(Vec3d::Zero()) @@ -1143,6 +1168,7 @@ GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec m_rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), m_rotation); m_scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), m_scaling_factor); } +#endif // ENABLE_MODELVOLUME_TRANSFORM GLCanvas3D::Selection::Selection() : m_volumes(nullptr) @@ -1171,16 +1197,31 @@ void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selectio if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) return; - // resets the current list if needed const GLVolume* volume = (*m_volumes)[volume_idx]; - if (as_single_selection || volume->is_wipe_tower || is_wipe_tower() || volume->is_modifier || is_modifier()) + // wipe tower is already selected + if (is_wipe_tower() && volume->is_wipe_tower) + return; + + // resets the current list if needed + bool needs_reset = as_single_selection; + needs_reset |= volume->is_wipe_tower; + needs_reset |= is_wipe_tower() && !volume->is_wipe_tower; + needs_reset |= !is_modifier() && volume->is_modifier; + needs_reset |= is_modifier() && !volume->is_modifier; + + if (needs_reset) clear(); + if (volume->is_modifier) + m_mode = Volume; + switch (m_mode) { case Volume: { - _add_volume(volume_idx); + if (is_empty() || (volume->instance_idx() == get_instance_idx())) + _add_volume(volume_idx); + break; } case Instance: @@ -1188,11 +1229,13 @@ void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selectio _add_instance(volume->object_idx(), volume->instance_idx()); break; } +#if !ENABLE_MODELVOLUME_TRANSFORM case Object: { _add_object(volume->object_idx()); break; } +#endif // !ENABLE_MODELVOLUME_TRANSFORM } _update_type(); @@ -1218,11 +1261,13 @@ void GLCanvas3D::Selection::remove(unsigned int volume_idx) _remove_instance(volume->object_idx(), volume->instance_idx()); break; } +#if !ENABLE_MODELVOLUME_TRANSFORM case Object: { _remove_object(volume->object_idx()); break; } +#endif // !ENABLE_MODELVOLUME_TRANSFORM } _update_type(); @@ -1238,6 +1283,8 @@ void GLCanvas3D::Selection::add_object(unsigned int object_idx, bool as_single_s if (as_single_selection) clear(); + m_mode = Instance; + _add_object(object_idx); _update_type(); @@ -1264,6 +1311,8 @@ void GLCanvas3D::Selection::add_instance(unsigned int object_idx, unsigned int i if (as_single_selection) clear(); + m_mode = Instance; + _add_instance(object_idx, instance_idx); _update_type(); @@ -1281,7 +1330,7 @@ void GLCanvas3D::Selection::remove_instance(unsigned int object_idx, unsigned in m_bounding_box_dirty = true; } -void GLCanvas3D::Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, bool as_single_selection) +void GLCanvas3D::Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection) { if (!m_valid) return; @@ -1290,11 +1339,16 @@ void GLCanvas3D::Selection::add_volume(unsigned int object_idx, unsigned int vol if (as_single_selection) clear(); + m_mode = Volume; + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) - _add_volume(i); + { + if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) + _add_volume(i); + } } _update_type(); @@ -1328,6 +1382,7 @@ void GLCanvas3D::Selection::clear() } m_list.clear(); + _update_type(); m_bounding_box_dirty = true; } @@ -1338,11 +1393,8 @@ bool GLCanvas3D::Selection::is_single_full_instance() const return true; int object_idx = m_valid ? get_object_idx() : -1; - if (object_idx != -1) - { - if ((object_idx != -1) && (object_idx < 1000)) - return m_model->objects[object_idx]->volumes.size() == m_list.size(); - } + if ((0 <= object_idx) && (object_idx < (int)m_model->objects.size())) + return m_model->objects[object_idx]->volumes.size() == m_list.size(); return false; } @@ -1364,6 +1416,12 @@ int GLCanvas3D::Selection::get_instance_idx() const return -1; } +const GLCanvas3D::Selection::InstanceIdxsList& GLCanvas3D::Selection::get_instance_idxs() const +{ + assert(m_cache.content.size() == 1); + return m_cache.content.begin()->second; +} + const GLVolume* GLCanvas3D::Selection::get_volume(unsigned int volume_idx) const { return (m_valid && (volume_idx < (unsigned int)m_volumes->size())) ? (*m_volumes)[volume_idx] : nullptr; @@ -1392,9 +1450,21 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement) for (unsigned int i : m_list) { +#if ENABLE_MODELVOLUME_TRANSFORM + if (m_mode == Instance) + (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + else if (m_mode == Volume) + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); +#else (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].get_position() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM } +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + m_bounding_box_dirty = true; } @@ -1406,18 +1476,93 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation) for (unsigned int i : m_list) { if (is_single_full_instance()) +#if ENABLE_MODELVOLUME_TRANSFORM + (*m_volumes)[i]->set_instance_rotation(rotation); +#else (*m_volumes)[i]->set_rotation(rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); +#if ENABLE_MODELVOLUME_TRANSFORM + if (m_mode == Instance) + { + // extracts rotations from the composed transformation + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); + (*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + (*m_volumes)[i]->set_instance_rotation(new_rotation); + } + else if (m_mode == Volume) + { + // extracts rotations from the composed transformation + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + (*m_volumes)[i]->set_volume_rotation(new_rotation); + } +#else // extracts rotations from the composed transformation Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_rotation_matrix()); - (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); (*m_volumes)[i]->set_rotation(new_rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM } } +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) +{ + // We get the normal in untransformed coordinates. We must transform it using the instance matrix, find out + // how to rotate the instance so it faces downwards and do the rotation. All that for all selected instances. + // The function assumes that is_from_single_object() holds. + + if (!m_valid) + return; + + for (unsigned int i : m_list) + { +#if ENABLE_MODELVOLUME_TRANSFORM + Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_volume_scale_matrix(); + Vec3d scaling_factor = Vec3d(1./wst(0,0), 1./wst(1,1), 1./wst(2,2)); + + Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_volume_rotation_matrix()); + Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; + transformed_normal.normalize(); + + Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); + axis.normalize(); + + Transform3d extra_rotation = Transform3d::Identity(); + extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); + + Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix() ); + (*m_volumes)[i]->set_instance_rotation(new_rotation); +#else + Transform3d wst = m_cache.volumes_data[i].get_scale_matrix() * m_cache.volumes_data[i].get_scale_matrix(); + Vec3d scaling_factor = Vec3d(1. / wst(0, 0), 1. / wst(1, 1), 1. / wst(2, 2)); + + Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_rotation_matrix() * m_cache.volumes_data[i].get_rotation_matrix()); + Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; + transformed_normal.normalize(); + + Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); + axis.normalize(); + + Transform3d extra_rotation = Transform3d::Identity(); + extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); + + Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_rotation_matrix()); + (*m_volumes)[i]->set_rotation(new_rotation); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) _synchronize_unselected_instances(); @@ -1426,6 +1571,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation) m_bounding_box_dirty = true; } + void GLCanvas3D::Selection::scale(const Vec3d& scale) { if (!m_valid) @@ -1434,22 +1580,45 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale) for (unsigned int i : m_list) { if (is_single_full_instance()) +#if ENABLE_MODELVOLUME_TRANSFORM + (*m_volumes)[i]->set_instance_scaling_factor(scale); +#else (*m_volumes)[i]->set_scaling_factor(scale); +#endif // ENABLE_MODELVOLUME_TRANSFORM else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); +#if ENABLE_MODELVOLUME_TRANSFORM + if (m_mode == Instance) + { + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + (*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + (*m_volumes)[i]->set_instance_scaling_factor(new_scale); + } + else if (m_mode == Volume) + { + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + (*m_volumes)[i]->set_volume_scaling_factor(new_scale); + } +#else Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); (*m_volumes)[i]->set_scaling_factor(new_scale); +#endif // ENABLE_MODELVOLUME_TRANSFORM } } #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; @@ -1460,15 +1629,25 @@ void GLCanvas3D::Selection::mirror(Axis axis) if (!m_valid) return; + bool single_full_instance = is_single_full_instance(); + for (unsigned int i : m_list) { - if (is_single_full_instance()) + if (single_full_instance) +#if ENABLE_MODELVOLUME_TRANSFORM + (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); + else if (m_mode == Volume) + (*m_volumes)[i]->set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); +#else (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis)); +#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) _synchronize_unselected_instances(); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; @@ -1483,7 +1662,11 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& disp { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == object_idx) +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM } std::set done; // prevent processing volumes twice @@ -1511,7 +1694,11 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& disp if (v->object_idx() != object_idx) continue; +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } @@ -1528,7 +1715,11 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM } std::set done; // prevent processing volumes twice @@ -1556,7 +1747,11 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) continue; +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_offset(v->get_instance_offset() + displacement); +#else v->set_offset(v->get_offset() + displacement); +#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } @@ -1564,17 +1759,14 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst m_bounding_box_dirty = true; } -void GLCanvas3D::Selection::render(bool show_indirect_selection) const +void GLCanvas3D::Selection::render() const { if (is_empty()) return; // render cumulative bounding box of selected volumes _render_selected_volumes(); - - // render bounding boxes of indirectly selected instances - if (show_indirect_selection && (m_mode == Instance)) - _render_unselected_instances(); + _render_synchronized_volumes(); } void GLCanvas3D::Selection::_update_valid() @@ -1599,6 +1791,8 @@ void GLCanvas3D::Selection::_update_type() obj_it->second.insert(inst_idx); } + bool requires_disable = false; + if (!m_valid) m_type = Invalid; else @@ -1611,7 +1805,10 @@ void GLCanvas3D::Selection::_update_type() if (first->is_wipe_tower) m_type = WipeTower; else if (first->is_modifier) - m_type = Modifier; + { + m_type = SingleModifier; + requires_disable = true; + } else { const ModelObject* model_object = m_model->objects[first->object_idx()]; @@ -1621,6 +1818,11 @@ void GLCanvas3D::Selection::_update_type() m_type = SingleFullObject; else if (volumes_count == 1) // instances_count > 1 m_type = SingleFullInstance; + else + { + m_type = SingleVolume; + requires_disable = true; + } } } else @@ -1630,14 +1832,47 @@ void GLCanvas3D::Selection::_update_type() const ModelObject* model_object = m_model->objects[m_cache.content.begin()->first]; unsigned int volumes_count = (unsigned int)model_object->volumes.size(); unsigned int instances_count = (unsigned int)model_object->instances.size(); + unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); if (volumes_count * instances_count == (unsigned int)m_list.size()) m_type = SingleFullObject; - else if ((m_cache.content.begin()->second.size() == 1) && (volumes_count == (unsigned int)m_list.size())) - m_type = SingleFullInstance; + else if (selected_instances_count == 1) + { + if (volumes_count == (unsigned int)m_list.size()) + m_type = SingleFullInstance; + else + { + unsigned int modifiers_count = 0; + for (unsigned int i : m_list) + { + if ((*m_volumes)[i]->is_modifier) + ++modifiers_count; + } + + if (modifiers_count == 0) + { + m_type = MultipleVolume; + requires_disable = true; + } + else if (modifiers_count == (unsigned int)m_list.size()) + { + m_type = MultipleModifier; + requires_disable = true; + } + } + } + else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) + m_type = MultipleFullInstance; } } } + int object_idx = get_object_idx(); + int instance_idx = get_instance_idx(); + for (GLVolume* v : *m_volumes) + { + v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false; + } + switch (m_type) { case Invalid: @@ -1655,9 +1890,24 @@ void GLCanvas3D::Selection::_update_type() std::cout << "selection type: WipeTower" << std::endl; break; } - case Modifier: + case SingleModifier: { - std::cout << "selection type: Modifier" << std::endl; + std::cout << "selection type: SingleModifier" << std::endl; + break; + } + case MultipleModifier: + { + std::cout << "selection type: MultipleModifier" << std::endl; + break; + } + case SingleVolume: + { + std::cout << "selection type: SingleVolume" << std::endl; + break; + } + case MultipleVolume: + { + std::cout << "selection type: MultipleVolume" << std::endl; break; } case SingleFullObject: @@ -1670,6 +1920,11 @@ void GLCanvas3D::Selection::_update_type() std::cout << "selection type: SingleFullInstance" << std::endl; break; } + case MultipleFullInstance: + { + std::cout << "selection type: MultipleFullInstance" << std::endl; + break; + } case Mixed: { std::cout << "selection type: Mixed" << std::endl; @@ -1684,7 +1939,11 @@ void GLCanvas3D::Selection::_set_caches() for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; +#if ENABLE_MODELVOLUME_TRANSFORM + m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); +#else m_cache.volumes_data.emplace(i, VolumeCache(v->get_offset(), v->get_rotation(), v->get_scaling_factor())); +#endif // ENABLE_MODELVOLUME_TRANSFORM } m_cache.dragging_center = get_bounding_box().center(); } @@ -1769,53 +2028,30 @@ void GLCanvas3D::Selection::_render_selected_volumes() const _render_bounding_box(get_bounding_box(), color); } -void GLCanvas3D::Selection::_render_unselected_instances() const +void GLCanvas3D::Selection::_render_synchronized_volumes() const { - std::set done; // prevent processing volumes twice - done.insert(m_list.begin(), m_list.end()); - - typedef std::map, BoundingBoxf3> InstanceToBoxMap; - InstanceToBoxMap boxes; - for (unsigned int i : m_list) - { - if (done.size() == m_volumes->size()) - break; - - const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); - if (object_idx >= 1000) - continue; - - int instance_idx = volume->instance_idx(); - - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { - if (done.size() == m_volumes->size()) - break; - - if (done.find(j) != done.end()) - continue; - - GLVolume* v = (*m_volumes)[j]; - int i_idx = v->instance_idx(); - if ((v->object_idx() != object_idx) || (i_idx == instance_idx)) - continue; - - std::pair box_id(object_idx, i_idx); - InstanceToBoxMap::iterator it = boxes.find(box_id); - if (it == boxes.end()) - it = boxes.insert(InstanceToBoxMap::value_type(box_id, BoundingBoxf3())).first; - - it->second.merge(v->transformed_convex_hull_bounding_box()); - - done.insert(j); - } - } + if (m_mode == Instance) + return; float color[3] = { 1.0f, 1.0f, 0.0f }; - for (const InstanceToBoxMap::value_type& box : boxes) + + for (unsigned int i : m_list) { - _render_bounding_box(box.second, color); + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + int volume_idx = volume->volume_idx(); + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (i == j) + continue; + + const GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + continue; + + _render_bounding_box(v->transformed_convex_hull_bounding_box(), color); + } } } @@ -1826,11 +2062,12 @@ void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float Vec3f b_min = box.min.cast(); Vec3f b_max = box.max.cast(); - Vec3f size = 0.25f * box.size().cast(); + Vec3f size = 0.2f * box.size().cast(); ::glEnable(GL_DEPTH_TEST); ::glColor3fv(color); + ::glLineWidth(2.0f); ::glBegin(GL_LINES); @@ -1885,9 +2122,15 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() continue; int instance_idx = volume->instance_idx(); +#if ENABLE_MODELVOLUME_TRANSFORM + const Vec3d& rotation = volume->get_instance_rotation(); + const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); + const Vec3d& mirror = volume->get_instance_mirror(); +#else const Vec3d& rotation = volume->get_rotation(); const Vec3d& scaling_factor = volume->get_scaling_factor(); const Vec3d& mirror = volume->get_mirror(); +#endif // ENABLE_MODELVOLUME_TRANSFORM // Process unselected instances. for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) @@ -1902,15 +2145,68 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) continue; - v->set_rotation(rotation); +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_instance_rotation(Vec3d(rotation(0), rotation(1), v->get_instance_rotation()(2))); + v->set_instance_scaling_factor(scaling_factor); + v->set_instance_mirror(mirror); +#else + v->set_rotation(Vec3d(rotation(0), rotation(1), v->get_rotation()(2))); v->set_scaling_factor(scaling_factor); v->set_mirror(mirror); +#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } } +void GLCanvas3D::Selection::_synchronize_unselected_volumes() +{ + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + int volume_idx = volume->volume_idx(); +#if ENABLE_MODELVOLUME_TRANSFORM + const Vec3d& offset = volume->get_volume_offset(); + const Vec3d& rotation = volume->get_volume_rotation(); + const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); + const Vec3d& mirror = volume->get_volume_mirror(); +#else + const Vec3d& offset = volume->get_offset(); + const Vec3d& rotation = volume->get_rotation(); + const Vec3d& scaling_factor = volume->get_scaling_factor(); + const Vec3d& mirror = volume->get_mirror(); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + // Process unselected volumes. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (j == i) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + continue; + +#if ENABLE_MODELVOLUME_TRANSFORM + v->set_volume_offset(offset); + v->set_volume_rotation(rotation); + v->set_volume_scaling_factor(scaling_factor); + v->set_volume_mirror(mirror); +#else + v->set_offset(offset); + v->set_rotation(Vec3d(rotation)); + v->set_scaling_factor(scaling_factor); + v->set_mirror(mirror); +#endif // ENABLE_MODELVOLUME_TRANSFORM + } + } +} + const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f; const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; @@ -2274,13 +2570,13 @@ void GLCanvas3D::Gizmos::set_rotation(const Vec3d& rotation) reinterpret_cast(it->second)->set_rotation(rotation); } -Vec3d GLCanvas3D::Gizmos::get_flattening_rotation() const +Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const { if (!m_enabled) return Vec3d::Zero(); GizmosMap::const_iterator it = m_gizmos.find(Flatten); - return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_rotation() : Vec3d::Zero(); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero(); } void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) @@ -2405,12 +2701,12 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - height += (float)it->second->get_textures_size(); - if (std::distance(it, m_gizmos.end()) > 1) - height += OverlayGapY; + if (it->first == SlaSupports && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) + continue; + height += (float)it->second->get_textures_size() + OverlayGapY; } - return height; + return height - OverlayGapY; } GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const @@ -3783,7 +4079,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.get_current_type() == Gizmos::Flatten) { // Rotate the object so the normal points downward: - m_selection.rotate(m_gizmos.get_flattening_rotation()); + m_selection.flattening_rotate(m_gizmos.get_flattening_normal()); _on_flatten(); wxGetApp().obj_manipul()->update_settings_value(m_selection); } @@ -4018,6 +4314,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #endif // ENABLE_GIZMOS_RESET { m_selection.clear(); + m_selection.set_mode(Selection::Instance); wxGetApp().obj_manipul()->update_settings_value(m_selection); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); _update_gizmos_data(); @@ -4643,9 +4940,8 @@ void GLCanvas3D::_render_objects() const void GLCanvas3D::_render_selection() const { - Gizmos::EType type = m_gizmos.get_current_type(); - bool show_indirect_selection = m_gizmos.is_running() && ((type == Gizmos::Rotate) || (type == Gizmos::Scale) || (type == Gizmos::Flatten)); - m_selection.render(show_indirect_selection); + if (!m_gizmos.is_running()) + m_selection.render(); } void GLCanvas3D::_render_cutting_plane() const @@ -4734,7 +5030,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glColor4fv(vol->render_color); } - vol->render(); + if (!fake_colors || !vol->disabled) + vol->render(); + ++volume_id; } @@ -4841,18 +5139,29 @@ void GLCanvas3D::_update_gizmos_data() if (m_selection.is_single_full_instance()) { +#if ENABLE_MODELVOLUME_TRANSFORM + // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; + m_gizmos.set_scale(volume->get_instance_scaling_factor()); + m_gizmos.set_rotation(volume->get_instance_rotation()); + ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; + m_gizmos.set_flattening_data(model_object); + m_gizmos.set_model_object_ptr(model_object); +#else ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; ModelInstance* model_instance = model_object->instances[m_selection.get_instance_idx()]; m_gizmos.set_scale(model_instance->get_scaling_factor()); m_gizmos.set_rotation(model_instance->get_rotation()); m_gizmos.set_flattening_data(model_object); m_gizmos.set_model_object_ptr(model_object); +#endif // ENABLE_MODELVOLUME_TRANSFORM } else { m_gizmos.set_scale(Vec3d::Ones()); m_gizmos.set_rotation(Vec3d::Zero()); - m_gizmos.set_flattening_data(nullptr); + m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr); + m_gizmos.set_model_object_ptr(nullptr); } } @@ -5969,38 +6278,57 @@ void GLCanvas3D::_on_move() if (m_model == nullptr) return; - std::set> done; // prevent moving instances twice + std::set> done; // keeps track of modified instances bool object_moved = false; Vec3d wipe_tower_origin = Vec3d::Zero(); + Selection::EMode selection_mode = m_selection.get_mode(); + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); - // prevent moving instances twice std::pair done_id(object_idx, instance_idx); - if (done.find(done_id) != done.end()) - continue; - if (object_idx < 1000) + if ((0 <= object_idx) && (object_idx < (int)m_model->objects.size())) { done.insert(done_id); - // Move instances. + // Move instances/volumes ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + { + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + object_moved = true; + } + else if (selection_mode == Selection::Volume) + { + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + object_moved = true; + } + if (object_moved) +#else model_object->instances[instance_idx]->set_offset(v->get_offset()); - model_object->invalidate_bounding_box(); object_moved = true; +#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); } } else if (object_idx == 1000) // Move a wipe tower proxy. +#if ENABLE_MODELVOLUME_TRANSFORM + wipe_tower_origin = v->get_volume_offset(); +#else wipe_tower_origin = v->get_offset(); +#endif // ENABLE_MODELVOLUME_TRANSFORM } + // Fixes sinking/flying instances for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; @@ -6021,33 +6349,45 @@ void GLCanvas3D::_on_rotate() if (m_model == nullptr) return; - std::set> done; // prevent rotating instances twice + std::set> done; // keeps track of modified instances + Selection::EMode selection_mode = m_selection.get_mode(); + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); - if (object_idx >= 1000) + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) continue; int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); - // prevent rotating instances twice - std::pair done_id(object_idx, instance_idx); - if (done.find(done_id) != done.end()) - continue; + done.insert(std::pair(object_idx, instance_idx)); - done.insert(done_id); - - // Rotate instances. + // Rotate instances/volumes. ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + { + model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation()); + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + } + else if (selection_mode == Selection::Volume) + { + model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation()); + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + } +#else model_object->instances[instance_idx]->set_rotation(v->get_rotation()); model_object->instances[instance_idx]->set_offset(v->get_offset()); +#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } + // Fixes sinking/flying instances for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; @@ -6064,33 +6404,45 @@ void GLCanvas3D::_on_scale() if (m_model == nullptr) return; - std::set> done; // prevent scaling instances twice + std::set> done; // keeps track of modified instances + + Selection::EMode selection_mode = m_selection.get_mode(); for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); - if (object_idx >= 1000) + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) continue; int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); - // prevent scaling instances twice - std::pair done_id(object_idx, instance_idx); - if (done.find(done_id) != done.end()) - continue; + done.insert(std::pair(object_idx, instance_idx)); - done.insert(done_id); - - // Rotate instances. + // Rotate instances/volumes ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + { + model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + } + else if (selection_mode == Selection::Volume) + { + model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + } +#else model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor()); model_object->instances[instance_idx]->set_offset(v->get_offset()); +#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } + // Fixes sinking/flying instances for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; @@ -6112,28 +6464,33 @@ void GLCanvas3D::_on_mirror() if (m_model == nullptr) return; - std::set> done; // prevent mirroring instances twice + std::set> done; // keeps track of modified instances + + Selection::EMode selection_mode = m_selection.get_mode(); for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); - if (object_idx >= 1000) + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) continue; int instance_idx = v->instance_idx(); + int volume_idx = v->volume_idx(); - // prevent mirroring instances twice - std::pair done_id(object_idx, instance_idx); - if (done.find(done_id) != done.end()) - continue; + done.insert(std::pair(object_idx, instance_idx)); - done.insert(done_id); - - // Mirror instances. + // Mirror instances/volumes ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection_mode == Selection::Instance) + model_object->instances[instance_idx]->set_mirror(v->get_instance_mirror()); + else if (selection_mode == Selection::Volume) + model_object->volumes[volume_idx]->set_mirror(v->get_volume_mirror()); +#else model_object->instances[instance_idx]->set_mirror(v->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index bf2fb4fd5..b1b78d3b4 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -358,9 +358,14 @@ public: enum EMode : unsigned char { +#if ENABLE_MODELVOLUME_TRANSFORM + Volume, + Instance +#else Volume, Instance, Object +#endif // ENABLE_MODELVOLUME_TRANSFORM }; enum EType : unsigned char @@ -368,9 +373,13 @@ public: Invalid, Empty, WipeTower, - Modifier, + SingleModifier, + MultipleModifier, + SingleVolume, + MultipleVolume, SingleFullObject, SingleFullInstance, + MultipleFullInstance, Mixed }; @@ -378,21 +387,57 @@ public: struct VolumeCache { private: +#if ENABLE_MODELVOLUME_TRANSFORM + struct TransformCache + { + Vec3d position; + Vec3d rotation; + Vec3d scaling_factor; + Transform3d rotation_matrix; + Transform3d scale_matrix; + + TransformCache(); + explicit TransformCache(const Geometry::Transformation& transform); + }; + + TransformCache m_volume; + TransformCache m_instance; +#else Vec3d m_position; Vec3d m_rotation; Vec3d m_scaling_factor; Transform3d m_rotation_matrix; Transform3d m_scale_matrix; +#endif // ENABLE_MODELVOLUME_TRANSFORM public: +#if ENABLE_MODELVOLUME_TRANSFORM + VolumeCache() {} + VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); +#else VolumeCache(); VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); +#endif // ENABLE_MODELVOLUME_TRANSFORM +#if ENABLE_MODELVOLUME_TRANSFORM + const Vec3d& get_volume_position() const { return m_volume.position; } + const Vec3d& get_volume_rotation() const { return m_volume.rotation; } + const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } + const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; } + const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; } + + const Vec3d& get_instance_position() const { return m_instance.position; } + const Vec3d& get_instance_rotation() const { return m_instance.rotation; } + const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; } + const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } + const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } +#else const Vec3d& get_position() const { return m_position; } const Vec3d& get_rotation() const { return m_rotation; } const Vec3d& get_scaling_factor() const { return m_scaling_factor; } const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } const Transform3d& get_scale_matrix() const { return m_scale_matrix; } +#endif // ENABLE_MODELVOLUME_TRANSFORM }; typedef std::map VolumesCache; @@ -435,14 +480,14 @@ public: void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); void remove_instance(unsigned int object_idx, unsigned int instance_idx); - void add_volume(unsigned int object_idx, unsigned int volume_idx, bool as_single_selection = true); + void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); void remove_volume(unsigned int object_idx, unsigned int volume_idx); void clear(); bool is_empty() const { return m_type == Empty; } bool is_wipe_tower() const { return m_type == WipeTower; } - bool is_modifier() const { return m_type == Modifier; } + bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } bool is_single_full_instance() const; bool is_single_full_object() const { return m_type == SingleFullObject; } bool is_mixed() const { return m_type == Mixed; } @@ -455,6 +500,9 @@ public: int get_object_idx() const; // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1 int get_instance_idx() const; + // Returns the indices of selected instances. + // Can only be called if selection is from a single object. + const InstanceIdxsList& get_instance_idxs() const; const IndicesList& get_volume_idxs() const { return m_list; } const GLVolume* get_volume(unsigned int volume_idx) const; @@ -466,13 +514,14 @@ public: void translate(const Vec3d& displacement); void rotate(const Vec3d& rotation); + void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale); void mirror(Axis axis); void translate(unsigned int object_idx, const Vec3d& displacement); void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); - void render(bool show_indirect_selection) const; + void render() const; private: void _update_valid(); @@ -486,9 +535,10 @@ public: void _remove_object(unsigned int object_idx); void _calc_bounding_box() const; void _render_selected_volumes() const; - void _render_unselected_instances() const; + void _render_synchronized_volumes() const; void _render_bounding_box(const BoundingBoxf3& box, float* color) const; void _synchronize_unselected_instances(); + void _synchronize_unselected_volumes(); }; private: @@ -556,7 +606,7 @@ private: Vec3d get_rotation() const; void set_rotation(const Vec3d& rotation); - Vec3d get_flattening_rotation() const; + Vec3d get_flattening_normal() const; void set_flattening_data(const ModelObject* model_object); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 2c8c7630a..3df089c73 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -720,7 +720,11 @@ void GLGizmoScale3D::on_process_double_click() void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const { bool single_instance = selection.is_single_full_instance(); +#if ENABLE_MODELVOLUME_TRANSFORM + Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast() : 100.0f * m_scale.cast(); +#else Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast() : 100.0f * m_scale.cast(); +#endif // ENABLE_MODELVOLUME_TRANSFORM if ((single_instance && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) set_tooltip("X: " + format(scale(0), 4) + "%"); @@ -762,10 +766,18 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const #endif // ENABLE_MODELVOLUME_TRANSFORM // gets angles from first selected volume +#if ENABLE_MODELVOLUME_TRANSFORM + angles = v->get_instance_rotation(); +#else angles = v->get_rotation(); +#endif // ENABLE_MODELVOLUME_TRANSFORM // consider rotation+mirror only components of the transform for offsets +#if ENABLE_MODELVOLUME_TRANSFORM + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); +#else offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM } else box = selection.get_bounding_box(); @@ -1165,38 +1177,41 @@ void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const { - // the dragged_offset is a vector measuring where was the object moved - // with the gizmo being on. This is reset in set_flattening_data and - // does not work correctly when there are multiple copies. + // The planes are rendered incorrectly when the object is being moved. We better won't render anything in that case. + // This indeed has a better solution (to be implemented when there is more time) Vec3d dragged_offset(Vec3d::Zero()); if (m_starting_center == Vec3d::Zero()) m_starting_center = selection.get_bounding_box().center(); dragged_offset = selection.get_bounding_box().center() - m_starting_center; + if (dragged_offset.norm() > 0.001) + return; ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); ::glDisable(GL_CULL_FACE); - for (int i=0; i<(int)m_planes.size(); ++i) { - if (i == m_hover_id) - ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); - else - ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); + if (selection.is_from_single_object()) { + const std::set& instances_list = selection.get_instance_idxs(); - int instance_idx = selection.get_instance_idx(); - if ((instance_idx != -1) && (m_model_object != nullptr)) - { + if (!instances_list.empty() && m_model_object) { + for (const int instance_idx : instances_list) { Transform3d m = m_model_object->instances[instance_idx]->get_matrix(); - m.pretranslate(dragged_offset); - ::glPushMatrix(); - ::glMultMatrixd(m.data()); - ::glBegin(GL_POLYGON); - for (const Vec3d& vertex : m_planes[i].vertices) - { - ::glVertex3dv(vertex.data()); + for (int i=0; i<(int)m_planes.size(); ++i) { + if (i == m_hover_id) + ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); + else + ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); + + m.pretranslate(dragged_offset); + ::glPushMatrix(); + ::glMultMatrixd(m.data()); + ::glBegin(GL_POLYGON); + for (const Vec3d& vertex : m_planes[i].vertices) + ::glVertex3dv(vertex.data()); + ::glEnd(); + ::glPopMatrix(); + } } - ::glEnd(); - ::glPopMatrix(); } } @@ -1208,22 +1223,21 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio { ::glEnable(GL_DEPTH_TEST); ::glDisable(GL_CULL_FACE); - - for (unsigned int i = 0; i < m_planes.size(); ++i) - { - ::glColor3f(1.0f, 1.0f, picking_color_component(i)); - int instance_idx = selection.get_instance_idx(); - if ((instance_idx != -1) && (m_model_object != nullptr)) - { - ::glPushMatrix(); - ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); - ::glBegin(GL_POLYGON); - for (const Vec3d& vertex : m_planes[i].vertices) - { - ::glVertex3dv(vertex.data()); + if (selection.is_from_single_object()) { + const std::set& instances_list = selection.get_instance_idxs(); + if (!instances_list.empty() && m_model_object) { + for (const int instance_idx : instances_list) { + for (int i=0; i<(int)m_planes.size(); ++i) { + ::glColor3f(1.0f, 1.0f, picking_color_component(i)); + ::glPushMatrix(); + ::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data()); + ::glBegin(GL_POLYGON); + for (const Vec3d& vertex : m_planes[i].vertices) + ::glVertex3dv(vertex.data()); + ::glEnd(); + ::glPopMatrix(); + } } - ::glEnd(); - ::glPopMatrix(); } } @@ -1233,9 +1247,10 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) { m_starting_center = Vec3d::Zero(); + bool object_changed = m_model_object != model_object; m_model_object = model_object; - if (is_plane_update_necessary()) + if (object_changed && is_plane_update_necessary()) update_planes(); } @@ -1243,7 +1258,15 @@ void GLGizmoFlatten::update_planes() { TriangleMesh ch; for (const ModelVolume* vol : m_model_object->volumes) +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh vol_ch = vol->get_convex_hull(); + vol_ch.transform(vol->get_matrix()); + ch.merge(vol_ch); + } +#else ch.merge(vol->get_convex_hull()); +#endif // ENABLE_MODELVOLUME_TRANSFORM ch = ch.convex_hull_3d(); @@ -1438,20 +1461,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const return false; } -Vec3d GLGizmoFlatten::get_flattening_rotation() const +Vec3d GLGizmoFlatten::get_flattening_normal() const { - // calculates the rotations in model space, taking in account the scaling factors - Eigen::Matrix m = m_model_object->instances.front()->get_matrix(true, true).matrix().block(0, 0, 3, 3).inverse().transpose(); - Eigen::Quaterniond q; - Vec3d angles = Geometry::extract_euler_angles(q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix()); + Vec3d out = m_normal; m_normal = Vec3d::Zero(); m_starting_center = Vec3d::Zero(); - return angles; + return out; } - - - GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent) : GLGizmoBase(parent), m_starting_center(Vec3d::Zero()) { @@ -1478,11 +1495,14 @@ bool GLGizmoSlaSupports::on_init() void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) { - m_starting_center = Vec3d::Zero(); - m_model_object = model_object; - m_model_object_matrix = model_object->instances.front()->get_matrix(); - if (is_mesh_update_necessary()) - update_mesh(); + if (model_object != nullptr) + { + m_starting_center = Vec3d::Zero(); + m_model_object = model_object; + m_model_object_matrix = model_object->instances.front()->get_matrix(); + if (is_mesh_update_necessary()) + update_mesh(); + } } void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index a2cf05ee3..727ef0868 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -359,6 +359,7 @@ private: std::vector m_planes; mutable Vec3d m_starting_center; const ModelObject* m_model_object = nullptr; + std::vector instances_matrices; void update_planes(); bool is_plane_update_necessary() const; @@ -367,12 +368,12 @@ public: explicit GLGizmoFlatten(GLCanvas3D& parent); void set_flattening_data(const ModelObject* model_object); - Vec3d get_flattening_rotation() const; + Vec3d get_flattening_normal() const; protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return selection.is_single_full_instance(); } + virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return (selection.is_from_single_object() && !selection.is_wipe_tower() && !selection.is_modifier()); } virtual void on_start_dragging(const GLCanvas3D::Selection& selection); virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) {} virtual void on_render(const GLCanvas3D::Selection& selection) const; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index ccc7026ee..3c52d29f6 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -60,7 +60,7 @@ enum ConfigMenuIDs { class Tab; -static wxString dots("…", wxConvUTF8); +static wxString dots("…", wxConvUTF8); class GUI_App : public wxApp { diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d1f9f4df1..63d9740ca 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -36,11 +36,11 @@ ObjectList::ObjectList(wxWindow* parent) : CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG); } - init_icons(); - // create control create_objects_ctrl(); + init_icons(); + // describe control behavior Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { selection_changed(); @@ -213,17 +213,27 @@ void ObjectList::update_extruder_in_config(const wxString& selection) void ObjectList::init_icons() { - m_bmp_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); - m_bmp_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); + m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); + m_bmp_solidmesh = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); + + m_bmp_support_enforcer = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG); + m_bmp_support_blocker = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG); + + m_bmp_vector.reserve(4); // bitmaps for different types of parts + m_bmp_vector.push_back(&m_bmp_solidmesh); // Add part + m_bmp_vector.push_back(&m_bmp_modifiermesh); // Add modifier + m_bmp_vector.push_back(&m_bmp_support_enforcer); // Add support enforcer + m_bmp_vector.push_back(&m_bmp_support_blocker); // Add support blocker + m_objects_model->SetVolumeBitmaps(m_bmp_vector); // init icon for manifold warning - m_bmp_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); + m_bmp_manifold_warning = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); // init bitmap for "Split to sub-objects" context menu - m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); + m_bmp_split = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG); // init bitmap for "Add Settings" context menu - m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); + m_bmp_cog = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG); } @@ -377,7 +387,8 @@ void ObjectList::on_drop(wxDataViewEvent &event) wxDataViewItem item(event.GetItem()); // only allow drops for item, not containers - if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || + if (m_selected_object_id < 0 || + item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) { event.Veto(); return; @@ -518,16 +529,24 @@ void ObjectList::get_settings_choice(wxMenu *menu, int id, bool is_part) wxGetApp().obj_manipul()->update_settings_list(); } -void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id) { +void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id, const int type) { auto sub_menu = new wxMenu; + const wxString menu_load = _(L("Load")) +" "+ dots; + sub_menu->Append(new wxMenuItem(sub_menu, id++, menu_load)); + sub_menu->AppendSeparator(); + std::vector menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; for (auto& item : menu_items) - sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item))); + sub_menu->Append(new wxMenuItem(sub_menu, id++, _(item))); #ifndef __WXMSW__ - sub_menu->Bind(wxEVT_MENU, [this, sub_menu](wxEvent &event) { - load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); + sub_menu->Bind(wxEVT_MENU, [sub_menu, type, menu_load, this](wxEvent &event) { + auto selection = sub_menu->GetLabel(event.GetId()); + if (selection == menu_load) + load_subobject(type); + else + load_generic_subobject(selection.ToStdString(), type); }); #endif //no __WXMSW__ @@ -552,55 +571,53 @@ wxMenuItem* ObjectList::menu_item_settings(wxMenu* menu, int id, const bool is_p wxMenu* ObjectList::create_add_part_popupmenu() { wxMenu *menu = new wxMenu; - std::vector menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; + // Note: id accords to type of the sub-object, so sequence of the menu items is important + std::vector menu_object_types_items = {L("Add part"), // ~ModelVolume::MODEL_PART + L("Add modifier"), // ~ModelVolume::PARAMETER_MODIFIER + L("Add support enforcer"), // ~ModelVolume::SUPPORT_ENFORCER + L("Add support bloker") }; // ~ModelVolume::SUPPORT_BLOCKER + + const int obj_types_count = menu_object_types_items.size(); + const int generics_count = 5; // "Load ...", "Box", "Cylinder", "Sphere", "Slab" - wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size() + 4 + 2); + wxWindowID config_id_base = NewControlId(generics_count*obj_types_count + 2); - int i = 0; - for (auto& item : menu_items) { - auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); - menu_item->SetBitmap(i == 0 ? m_bmp_solidmesh : m_bmp_modifiermesh); - if (item == "Add generic") - menu_item_add_generic(menu_item, config_id_base + i); + // Add first 4 menu items + for (int type = 0; type < obj_types_count; type++) { + auto& item = menu_object_types_items[type]; + auto menu_item = new wxMenuItem(menu, config_id_base + type, _(item)); + menu_item->SetBitmap(*m_bmp_vector[type]); + menu_item_add_generic(menu_item, config_id_base + type*generics_count, type); menu->Append(menu_item); - i++; } + // Split object to parts menu->AppendSeparator(); - auto menu_item = menu_item_split(menu, config_id_base + i + 4); + auto menu_item = menu_item_split(menu, config_id_base + obj_types_count * generics_count); menu->Append(menu_item); menu_item->Enable(is_splittable_object(false)); + // Settings menu->AppendSeparator(); // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); + menu->Append(menu_item_settings(menu, config_id_base + obj_types_count * generics_count+1, false)); - menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { - switch (event.GetId() - config_id_base) { - case 0: - load_subobject(); - break; - case 1: - load_subobject(true); - break; - case 2: - case 3: - case 4: - case 5: - case 6: -#ifdef __WXMSW__ - load_lambda(menu->GetLabel(event.GetId()).ToStdString()); -#endif // __WXMSW__ - break; - case 7: //3: + menu->Bind(wxEVT_MENU, [config_id_base, menu, obj_types_count, generics_count, this](wxEvent &event) { + auto selection = event.GetId() - config_id_base; + + if ( selection == 0 * generics_count || // ~ModelVolume::MODEL_PART + selection == 1 * generics_count || // ~ModelVolume::PARAMETER_MODIFIER + selection == 2 * generics_count || // ~ModelVolume::SUPPORT_ENFORCER + selection == 3 * generics_count ) // ~ModelVolume::SUPPORT_BLOCKER + load_subobject(int(selection / generics_count)); + else if ( selection == obj_types_count * generics_count) split(false); - break; - default: #ifdef __WXMSW__ + else if ( selection > obj_types_count * generics_count) // "Add Settings" is selected get_settings_choice(menu, event.GetId(), false); + else // Some generic model is selected + load_generic_subobject(menu->GetLabel(event.GetId()).ToStdString(), int(selection / generics_count)); #endif // __WXMSW__ - break; - } }); return menu; @@ -609,24 +626,33 @@ wxMenu* ObjectList::create_add_part_popupmenu() wxMenu* ObjectList::create_part_settings_popupmenu() { wxMenu *menu = new wxMenu; - wxWindowID config_id_base = wxWindow::NewControlId(2); + wxWindowID config_id_base = NewControlId(3); auto menu_item = menu_item_split(menu, config_id_base); menu->Append(menu_item); menu_item->Enable(is_splittable_object(true)); + // Append change part type menu->AppendSeparator(); + menu->Append(new wxMenuItem(menu, config_id_base + 1, _(L("Change type")))); + // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + 1, true)); + menu->AppendSeparator(); + menu_item = menu_item_settings(menu, config_id_base + 2, true); + menu->Append(menu_item); + menu_item->Enable(get_selected_model_volume()->type() <= ModelVolume::PARAMETER_MODIFIER); menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) { switch (event.GetId() - config_id_base) { case 0: split(true); break; - default:{ + case 1: + change_part_type(); + break; + default: get_settings_choice(menu, event.GetId(), true); - break; } + break; } }); @@ -655,31 +681,21 @@ wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part) return menu; } - -// Load SubObjects (parts and modifiers) -void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = false*/) +void ObjectList::load_subobject(int type) { auto item = GetSelection(); - if (!item) - return; - int obj_idx = -1; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) - obj_idx = m_objects_model->GetIdByItem(item); - else + if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0)) return; + int obj_idx = m_objects_model->GetIdByItem(item); if (obj_idx < 0) return; wxArrayString part_names; - if (is_lambda) - load_lambda((*m_objects)[obj_idx], part_names, is_modifier); - else - load_part((*m_objects)[obj_idx], part_names, is_modifier); + load_part((*m_objects)[obj_idx], part_names, type); parts_changed(obj_idx); for (int i = 0; i < part_names.size(); ++i) { - const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), - is_modifier ? m_bmp_modifiermesh : m_bmp_solidmesh); + const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), /**m_bmp_vector[*/type/*]*/); if (i == part_names.size() - 1) select_item(sel_item); @@ -688,11 +704,12 @@ void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME // selection_changed(); #endif //no __WXOSX__//__WXMSW__ + } void ObjectList::load_part( ModelObject* model_object, wxArrayString& part_names, - const bool is_modifier) + int type) { wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); @@ -717,19 +734,27 @@ void ObjectList::load_part( ModelObject* model_object, if (model_object->origin_translation != Vec3d::Zero()) { object->center_around_origin(); +#if !ENABLE_MODELVOLUME_TRANSFORM object->ensure_on_bed(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM delta = model_object->origin_translation - object->origin_translation; } for (auto volume : object->volumes) { +#if ENABLE_MODELVOLUME_TRANSFORM + Vec3d shift = volume->mesh.bounding_box().center(); + volume->translate_geometry(-shift); + volume->translate(delta + shift); +#endif // ENABLE_MODELVOLUME_TRANSFORM auto new_volume = model_object->add_volume(*volume); - new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); - boost::filesystem::path(input_file).filename().string(); + new_volume->set_type(static_cast(type)); new_volume->name = boost::filesystem::path(input_file).filename().string(); part_names.Add(new_volume->name); +#if !ENABLE_MODELVOLUME_TRANSFORM if (delta != Vec3d::Zero()) new_volume->translate(delta); +#endif // !ENABLE_MODELVOLUME_TRANSFORM // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -738,98 +763,46 @@ void ObjectList::load_part( ModelObject* model_object, } } } + } -void ObjectList::load_lambda( ModelObject* model_object, - wxArrayString& part_names, - const bool is_modifier) +void ObjectList::load_generic_subobject(const std::string& type_name, const int type) { - auto dlg = new LambdaObjectDialog(GetMainWindow()); - if (dlg->ShowModal() == wxID_CANCEL) { - m_parts_changed = false; - return; - } - - std::string name = "lambda-"; - TriangleMesh mesh; - - auto params = dlg->ObjectParameters(); - switch (params.type) - { - case LambdaTypeBox:{ - mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); - name += "Box"; - break; } - case LambdaTypeCylinder:{ - mesh = make_cylinder(params.cyl_r, params.cyl_h); - name += "Cylinder"; - break; } - case LambdaTypeSphere:{ - mesh = make_sphere(params.sph_rho); - name += "Sphere"; - break; } - case LambdaTypeSlab:{ - const auto& size = model_object->bounding_box().size(); - mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); - // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z - mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); - name += "Slab"; - break; } - default: - break; - } - mesh.repair(); - - auto new_volume = model_object->add_volume(mesh); - new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); - - new_volume->name = name; - // set a default extruder value, since user can't add it manually - new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - - part_names.Add(name); - - m_parts_changed = true; -} - -void ObjectList::load_lambda(const std::string& type_name) -{ - if (m_selected_object_id < 0) return; - - auto dlg = new LambdaObjectDialog(GetMainWindow(), type_name); - if (dlg->ShowModal() == wxID_CANCEL) - return; + const auto obj_idx = get_selected_obj_idx(); + if (obj_idx < 0) return; const std::string name = "lambda-" + type_name; TriangleMesh mesh; - const auto params = dlg->ObjectParameters(); + auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option("bed_shape")->values; + const auto& sz = BoundingBoxf(bed_shape).size(); + const auto side = 0.1 * std::max(sz(0), sz(1)); + if (type_name == _("Box")) - mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); + mesh = make_cube(side, side, side); else if (type_name == _("Cylinder")) - mesh = make_cylinder(params.cyl_r, params.cyl_h); + mesh = make_cylinder(0.5*side, side); else if (type_name == _("Sphere")) - mesh = make_sphere(params.sph_rho); + mesh = make_sphere(side, PI/18); else if (type_name == _("Slab")) { - const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); - mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); + const auto& size = (*m_objects)[obj_idx]->bounding_box().size(); + mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5); // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z - mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); + mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, 0); } mesh.repair(); - auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); - new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); + auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); + new_volume->set_type(static_cast(type)); new_volume->name = name; // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); m_parts_changed = true; - parts_changed(m_selected_object_id); + parts_changed(obj_idx); - select_item(m_objects_model->AddVolumeChild(GetSelection(), - name, m_bmp_modifiermesh)); + select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type)); #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); #endif //no __WXOSX__ //__WXMSW__ @@ -927,8 +900,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con void ObjectList::split(const bool split_part) { const auto item = GetSelection(); - if (!item || m_selected_object_id < 0) + const int obj_idx = get_selected_obj_idx(); + if (!item || obj_idx < 0) return; + ModelVolume* volume; if (!get_volume_by_item(split_part, item, volume)) return; DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config; @@ -938,52 +913,54 @@ void ObjectList::split(const bool split_part) return; } - auto model_object = (*m_objects)[m_selected_object_id]; + auto model_object = (*m_objects)[obj_idx]; - if (split_part) { - auto parent = m_objects_model->GetParent(item); - m_objects_model->DeleteChildren(parent); + auto parent = m_objects_model->GetTopParent(item); + if (parent) + m_objects_model->DeleteVolumeChildren(parent); + else + parent = item; - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, - model_object->volumes[id]->is_modifier() ? m_bmp_modifiermesh : m_bmp_solidmesh, - model_object->volumes[id]->config.has("extruder") ? - model_object->volumes[id]->config.option("extruder")->value : 0, - false); + for (auto id = 0; id < model_object->volumes.size(); id++) { + const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, + model_object->volumes[id]->is_modifier() ? + ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART, + model_object->volumes[id]->config.has("extruder") ? + model_object->volumes[id]->config.option("extruder")->value : 0, + false); + // add settings to the part, if it has those + auto opt_keys = model_object->volumes[id]->config.keys(); + if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { + select_item(m_objects_model->AddSettingsChild(vol_item)); + Collapse(vol_item); + } + } + if (parent == item) Expand(parent); - } - else { - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, - m_bmp_solidmesh, - model_object->volumes[id]->config.has("extruder") ? - model_object->volumes[id]->config.option("extruder")->value : 0, - false); - Expand(item); - } m_parts_changed = true; - parts_changed(m_selected_object_id); - - // restores selection - _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection().add_object(m_selected_object_id); + parts_changed(obj_idx); } bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) { - if (!item || m_selected_object_id < 0) + auto obj_idx = get_selected_obj_idx(); + if (!item || obj_idx < 0) return false; const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + + // object is selected if (volume_id < 0) { - if (split_part) return false; - volume = (*m_objects)[m_selected_object_id]->volumes[0]; + if ( split_part || (*m_objects)[obj_idx]->volumes.size() > 1 ) + return false; + volume = (*m_objects)[obj_idx]->volumes[0]; } + // volume is selected else - volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - if (volume) - return true; - return false; + volume = (*m_objects)[obj_idx]->volumes[volume_id]; + + return true; } bool ObjectList::is_splittable_object(const bool split_part) @@ -991,20 +968,14 @@ bool ObjectList::is_splittable_object(const bool split_part) const wxDataViewItem item = GetSelection(); if (!item) return false; - wxDataViewItemArray children; - if (!split_part && m_objects_model->GetChildren(item, children) > 0) - return false; - ModelVolume* volume; if (!get_volume_by_item(split_part, item, volume) || !volume) return false; TriangleMeshPtrs meshptrs = volume->mesh.split(); bool splittable = meshptrs.size() > 1; - for (TriangleMesh* m : meshptrs) - { - delete m; - } + for (TriangleMesh* m : meshptrs) { delete m; } + return splittable; } @@ -1017,7 +988,7 @@ void ObjectList::part_settings_changed() void ObjectList::parts_changed(int obj_idx) { - wxGetApp().plater()->changed_object(get_selected_obj_idx()); + wxGetApp().plater()->changed_object(obj_idx); m_parts_changed = false; } @@ -1105,19 +1076,28 @@ void ObjectList::add_object_to_list(size_t obj_idx) m_objects_model->SetValue(variant, item, 0); } + // add volumes to the object if (model_object->volumes.size() > 1) { for (auto id = 0; id < model_object->volumes.size(); id++) m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, - m_bmp_solidmesh, + ModelVolume::MODEL_PART, model_object->volumes[id]->config.option("extruder")->value, false); Expand(item); } + // add instances to the object, if it has those if (model_object->instances.size()>1) increase_object_instances(obj_idx, model_object->instances.size()); + // add settings to the object, if it has those + auto opt_keys = model_object->config.keys(); + if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { + select_item(m_objects_model->AddSettingsChild(item)); + Collapse(item); + } + #ifndef __WXOSX__ selection_changed(); #endif //__WXMSW__ @@ -1227,11 +1207,18 @@ void ObjectList::update_selections() auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection(); wxDataViewItemArray sels; - for (auto idx: selection.get_volume_idxs()) - { - const auto gl_vol = selection.get_volume(idx); - sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); + if (selection.is_single_full_object()) { + for (auto idx : selection.get_volume_idxs()) { + const auto gl_vol = selection.get_volume(idx); + sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); + } } + else if (selection.is_single_full_instance()) { + for (auto idx : selection.get_instance_idxs()) { + sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx)); + } + } + select_items(sels); } @@ -1256,7 +1243,7 @@ void ObjectList::update_selections_on_canvas() if (m_objects_model->GetItemType(item) == itVolume) { const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); const int vol_idx = m_objects_model->GetVolumeIdByItem(item); - selection.add_volume(obj_idx, vol_idx, as_single_selection); + selection.add_volume(obj_idx, vol_idx, 0, as_single_selection); } else if (m_objects_model->GetItemType(item) == itInstance) { const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); @@ -1334,5 +1321,53 @@ void ObjectList::fix_multiselection_conflicts() m_prevent_list_events = false; } +ModelVolume* ObjectList::get_selected_model_volume() +{ + auto item = GetSelection(); + if (!item || m_objects_model->GetItemType(item) != itVolume) + return nullptr; + + const auto vol_idx = m_objects_model->GetVolumeIdByItem(item); + const auto obj_idx = get_selected_obj_idx(); + if (vol_idx < 0 || obj_idx < 0) + return nullptr; + + return (*m_objects)[obj_idx]->volumes[vol_idx]; +} + +void ObjectList::change_part_type() +{ + ModelVolume* volume = get_selected_model_volume(); + if (!volume) + return; + const auto type = volume->type(); + + const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" }; + + auto new_type = wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), type); + + if (new_type == type || new_type < 0) + return; + + const auto item = GetSelection(); + volume->set_type(static_cast(new_type)); + m_objects_model->SetVolumeType(item, new_type); + + m_parts_changed = true; + parts_changed(get_selected_obj_idx()); + + // Update settings showing, if we have it + //(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer) + const auto settings_item = m_objects_model->GetSettingsItem(item); + if (settings_item && + new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER) { + m_objects_model->Delete(settings_item); + } + else if (!settings_item && + new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER) { + select_item(m_objects_model->AddSettingsChild(item)); + } +} + } //namespace GUI } //namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 3a7de9e3b..33b2a7608 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -28,10 +28,14 @@ class ObjectList : public wxDataViewCtrl wxBitmap m_bmp_modifiermesh; wxBitmap m_bmp_solidmesh; + wxBitmap m_bmp_support_enforcer; + wxBitmap m_bmp_support_blocker; wxBitmap m_bmp_manifold_warning; wxBitmap m_bmp_cog; wxBitmap m_bmp_split; + std::vector m_bmp_vector; + int m_selected_object_id = -1; bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select() // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler @@ -78,17 +82,16 @@ public: void on_drop(wxDataViewEvent &event); void get_settings_choice(wxMenu *menu, int id, bool is_part); - void menu_item_add_generic(wxMenuItem* &menu, int id); + void menu_item_add_generic(wxMenuItem* &menu, int id, const int type); wxMenuItem* menu_item_split(wxMenu* menu, int id); wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part); wxMenu* create_add_part_popupmenu(); wxMenu* create_part_settings_popupmenu(); wxMenu* create_add_settings_popupmenu(bool is_part); - void load_subobject(bool is_modifier = false, bool is_lambda = false); - void load_part(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); - void load_lambda(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier); - void load_lambda(const std::string& type_name); + void load_subobject(int type); + void load_part(ModelObject* model_object, wxArrayString& part_names, int type); + void load_generic_subobject(const std::string& type_name, const int type); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(); void del_instances_from_object(const int obj_idx); @@ -142,6 +145,9 @@ public: void select_all(); // correct current selections to avoid of the possible conflicts void fix_multiselection_conflicts(); + + ModelVolume* get_selected_model_volume(); + void change_part_type(); }; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index fc34c0d09..806ea5699 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -268,6 +268,9 @@ void ObjectManipulation::update_settings_list() void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) { +#if ENABLE_MODELVOLUME_TRANSFORM + if (selection.is_single_full_instance()) +#else if (selection.is_single_full_object()) { auto obj_idx = selection.get_object_idx(); @@ -284,30 +287,49 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele reset_settings_value(); } else if (selection.is_single_full_instance()) +#endif // ENABLE_MODELVOLUME_TRANSFORM { // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + update_position_value(volume->get_instance_offset()); + update_rotation_value(volume->get_instance_rotation()); + update_scale_value(volume->get_instance_scaling_factor()); +#else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); update_scale_value(volume->get_scaling_factor()); +#endif // ENABLE_MODELVOLUME_TRANSFORM m_og->enable(); } else if (selection.is_wipe_tower()) { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + update_position_value(volume->get_volume_offset()); + update_rotation_value(volume->get_volume_rotation()); + update_scale_value(volume->get_volume_scaling_factor()); +#else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); update_scale_value(volume->get_scaling_factor()); +#endif // ENABLE_MODELVOLUME_TRANSFORM m_og->enable(); } else if (selection.is_modifier()) { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_MODELVOLUME_TRANSFORM + update_position_value(volume->get_volume_offset()); + update_rotation_value(volume->get_volume_rotation()); + update_scale_value(volume->get_volume_scaling_factor()); +#else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); update_scale_value(volume->get_scaling_factor()); +#endif // ENABLE_MODELVOLUME_TRANSFORM m_og->enable(); } else diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 5618eab04..78f8b7462 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1,5 +1,6 @@ #include "../../libslic3r/libslic3r.h" #include "GUI_Preview.hpp" +#include "GUI_App.hpp" #include "GUI.hpp" #include "AppConfig.hpp" #include "3DScene.hpp" @@ -22,6 +23,7 @@ namespace Slic3r { namespace GUI { + Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data) : m_canvas(nullptr) , m_double_slider_sizer(nullptr) @@ -482,6 +484,11 @@ void Preview::create_double_slider() if (IsShown()) m_canvas->Refresh(); }); + + Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + auto& config = wxGetApp().preset_bundle->project_config; + ((config.option("colorprint_heights"))->values) = (m_slider->GetTicksValues()); + }); } void Preview::update_double_slider(bool force_sliders_full_range) @@ -495,6 +502,11 @@ void Preview::update_double_slider(bool force_sliders_full_range) m_slider->SetMaxValue(layers_z.size() - 1); m_slider->SetSliderValues(values); + const auto& config = wxGetApp().preset_bundle->project_config; + const std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; + + m_slider->SetTicksValues(ticks_from_config); + set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); } @@ -515,6 +527,15 @@ void Preview::fill_slider_values(std::vector> &values, break; } } + + // All ticks that would end up outside the slider range should be erased. + // TODO: this should probably be placed into more appropriate part of code, + // this way it relies on the Preview tab being active. + auto& config = wxGetApp().preset_bundle->project_config; + std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; + ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), + [values](double val) { return values.back().second < val; }), + ticks_from_config.end()); } void Preview::set_double_slider_thumbs(const bool force_sliders_full_range, diff --git a/src/slic3r/GUI/LambdaObjectDialog.cpp b/src/slic3r/GUI/LambdaObjectDialog.cpp index a55a5bc9b..9c89a8c04 100644 --- a/src/slic3r/GUI/LambdaObjectDialog.cpp +++ b/src/slic3r/GUI/LambdaObjectDialog.cpp @@ -49,15 +49,15 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, def.type = coFloat; def.default_value = new ConfigOptionFloat{ 1.0 }; - def.label = L("L"); + def.label = L("Length"); Option option(def, "l"); optgroup->append_single_option_line(option); - def.label = L("W"); + def.label = L("Width"); option = Option(def, "w"); optgroup->append_single_option_line(option); - def.label = L("H"); + def.label = L("Height"); option = Option(def, "h"); optgroup->append_single_option_line(option); } @@ -112,7 +112,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, def.type = coFloat; def.default_value = new ConfigOptionFloat{ 1.0 }; - def.label = L("H"); + def.label = L("Height"); auto option = Option(def, "slab_h"); optgroup->append_single_option_line(option); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 1515b1386..a378a067e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -596,8 +596,7 @@ void MainFrame::load_config_file(wxString file/* = wxEmptyString*/) // if (Slic3r::GUI::catch_error(this)) // return; } - for (auto tab : m_options_tabs ) - tab.second->load_current_preset(); + wxGetApp().load_current_presets(); wxGetApp().app_config->update_config_dir(get_dir_name(file)); m_last_config = file; } @@ -659,8 +658,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re } // Load the currently selected preset into the GUI, update the preset selection box. - for (auto tab : m_options_tabs) - tab.second->load_current_preset(); + wxGetApp().load_current_presets(); const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported); Slic3r::GUI::show_info(this, message, "Info"); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 8e810fcda..a61713aac 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -22,18 +22,18 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co // is the normal type. if (opt.gui_type.compare("select") == 0) { } else if (opt.gui_type.compare("select_open") == 0) { - m_fields.emplace(id, STDMOVE(Choice::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(Choice::Create(parent(), opt, id))); } else if (opt.gui_type.compare("color") == 0) { - m_fields.emplace(id, STDMOVE(ColourPicker::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(ColourPicker::Create(parent(), opt, id))); } else if (opt.gui_type.compare("f_enum_open") == 0 || opt.gui_type.compare("i_enum_open") == 0 || opt.gui_type.compare("i_enum_closed") == 0) { - m_fields.emplace(id, STDMOVE(Choice::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(Choice::Create(parent(), opt, id))); } else if (opt.gui_type.compare("slider") == 0) { - m_fields.emplace(id, STDMOVE(SliderCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(SliderCtrl::Create(parent(), opt, id))); } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl } else if (opt.gui_type.compare("legend") == 0) { // StaticText - m_fields.emplace(id, STDMOVE(StaticText::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(StaticText::Create(parent(), opt, id))); } else { switch (opt.type) { case coFloatOrPercent: @@ -43,21 +43,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co case coPercents: case coString: case coStrings: - m_fields.emplace(id, STDMOVE(TextCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(TextCtrl::Create(parent(), opt, id))); break; case coBool: case coBools: - m_fields.emplace(id, STDMOVE(CheckBox::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(CheckBox::Create(parent(), opt, id))); break; case coInt: case coInts: - m_fields.emplace(id, STDMOVE(SpinCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(SpinCtrl::Create(parent(), opt, id))); break; case coEnum: - m_fields.emplace(id, STDMOVE(Choice::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(Choice::Create(parent(), opt, id))); break; case coPoints: - m_fields.emplace(id, STDMOVE(PointCtrl::Create(parent(), opt, id))); + m_fields.emplace(id, std::move(PointCtrl::Create(parent(), opt, id))); break; case coNone: break; default: diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index af6c08d7c..8cc120e1e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1154,16 +1154,25 @@ std::vector Plater::priv::load_files(const std::vector &input_ try { if (type_3mf || type_zip_amf) { DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - model = Slic3r::Model::read_from_archive(path.string(), &config, false); - Preset::normalize(config); - wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); - for (const auto &kv : main_frame->options_tabs()) { kv.second->load_current_preset(); } + { + DynamicPrintConfig config_loaded; + model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, false); + if (! config_loaded.empty()) { + // Based on the printer technology field found in the loaded config, select the base for the config, + PrinterTechnology printer_technology = Preset::printer_technology(config_loaded); + config.apply(printer_technology == ptFFF ? + static_cast(FullPrintConfig::defaults()) : + static_cast(SLAFullPrintConfig::defaults())); + // and place the loaded config over the base. + config += std::move(config_loaded); + } + } + if (! config.empty()) { + Preset::normalize(config); + wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); + wxGetApp().load_current_presets(); + } wxGetApp().app_config->update_config_dir(path.parent_path().string()); - // forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer - // and if the config contains a "layer_height" different from the current defined one - // TODO: - // $self->async_apply_config; } else { model = Slic3r::Model::read_from_file(path.string(), nullptr, false); for (auto obj : model.objects) @@ -1230,7 +1239,9 @@ std::vector Plater::priv::load_files(const std::vector &input_ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) { const BoundingBoxf bed_shape = bed_shape_bb(); +#if !ENABLE_MODELVOLUME_TRANSFORM const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); +#endif // !ENABLE_MODELVOLUME_TRANSFORM const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0); bool need_arrange = false; @@ -1249,8 +1260,12 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode // add a default instance and center object around origin object->center_around_origin(); // also aligns object to Z = 0 - auto *instance = object->add_instance(); + ModelInstance* instance = object->add_instance(); +#if ENABLE_MODELVOLUME_TRANSFORM + instance->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -object->origin_translation(2))); +#else instance->set_offset(bed_center); +#endif // ENABLE_MODELVOLUME_TRANSFORM } const Vec3d size = object->bounding_box().size(); @@ -1438,7 +1453,9 @@ void Plater::priv::mirror(Axis axis) void Plater::priv::arrange() { + this->background_process.stop(); main_frame->app_controller()->arrange_model(); + this->schedule_background_process(); // ignore arrange failures on purpose: user has visual feedback and we don't need to warn him // when parts don't fit in print bed @@ -1471,13 +1488,7 @@ void Plater::priv::split_object() { unsigned int counter = 1; for (ModelObject* m : new_objects) - { m->name = current_model_object->name + "_" + std::to_string(counter++); - for (ModelInstance* i : current_model_object->instances) - { - m->add_instance(*i); - } - } remove(obj_idx); @@ -2108,7 +2119,8 @@ void Plater::export_amf() wxString path = dialog->GetPath(); auto path_cstr = path.c_str(); - if (Slic3r::store_amf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); + if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { @@ -2127,7 +2139,8 @@ void Plater::export_3mf() wxString path = dialog->GetPath(); auto path_cstr = path.c_str(); - if (Slic3r::store_3mf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) { + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config(); + if (Slic3r::store_3mf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); } else { @@ -2256,7 +2269,9 @@ void Plater::changed_object(int obj_idx) if (list->is_parts_changed()) { // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; +#if !ENABLE_MODELVOLUME_TRANSFORM model_object->center_around_origin(); +#endif // !ENABLE_MODELVOLUME_TRANSFORM model_object->ensure_on_bed(); _3DScene::reload_scene(p->canvas3D, false); } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index bd43d01ca..892b391c2 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -170,10 +170,12 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr { const auto &defaults = FullPrintConfig::defaults(); for (const std::string &key : Preset::nozzle_options()) { + if (key == "default_filament_profile") + continue; auto *opt = config.option(key, false); assert(opt != nullptr); assert(opt->is_vector()); - if (opt != nullptr && opt->is_vector() && key != "default_filament_profile") + if (opt != nullptr && opt->is_vector()) static_cast(opt)->resize(num_extruders, defaults.option(key)); } } @@ -202,8 +204,7 @@ void Preset::normalize(DynamicPrintConfig &config) // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately. for (const std::string &key : { "filament_settings_id" }) { auto *opt = config.option(key, false); - assert(opt != nullptr); - assert(opt->type() == coStrings); + assert(opt == nullptr || opt->type() == coStrings); if (opt != nullptr && opt->type() == coStrings) static_cast(opt)->values.resize(n, std::string()); } @@ -534,7 +535,8 @@ Preset& PresetCollection::load_external_preset( cfg.apply_only(config, cfg.keys(), true); // Is there a preset already loaded with the name stored inside the config? std::deque::iterator it = this->find_preset_internal(original_name); - if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) { + bool found = it != m_presets.end() && it->name == original_name; + if (found && profile_print_params_same(it->config, cfg)) { // The preset exists and it matches the values stored inside config. if (select) this->select_preset(it - m_presets.begin()); @@ -542,7 +544,7 @@ Preset& PresetCollection::load_external_preset( } // Update the "inherits" field. std::string &inherits = Preset::inherits(cfg); - if (it != m_presets.end() && inherits.empty()) { + if (found && inherits.empty()) { // There is a profile with the same name already loaded. Should we update the "inherits" field? if (it->vendor == nullptr) inherits = it->inherits(); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index f22299b7c..f51e9f647 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -36,6 +36,7 @@ namespace Slic3r { static std::vector s_project_options { + "colorprint_heights", "wiping_volumes_extruders", "wiping_volumes_matrix" }; @@ -617,18 +618,6 @@ void PresetBundle::load_config_file(const std::string &path) } } -void PresetBundle::load_config_string(const char* str, const char* source_filename) -{ - if (str != nullptr) - { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config.load_from_gcode_string(str); - Preset::normalize(config); - load_config_file_config((source_filename == nullptr) ? "" : source_filename, true, std::move(config)); - } -} - // Load a config file from a boost property_tree. This is a private method called from load_config_file. void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) { @@ -676,7 +665,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool compatible_printers_condition = compatible_printers_condition_values[idx]; if (is_external) presets.load_external_preset(name_or_path, name, - config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") : "printer_settings_id", true), + config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_settings_id") : "printer_settings_id", true), config); else presets.load_preset(presets.path_from_name(name), name, config).save(); diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index a7d9ab7d2..07f89bc70 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -84,11 +84,6 @@ public: // If the file is loaded successfully, its print / filament / printer profiles will be activated. void load_config_file(const std::string &path); - // Load an external config source containing the print, filament and printer presets. - // The given string must contain the full set of parameters (same as those exported to gcode). - // If the string is parsed successfully, its print / filament / printer profiles will be activated. - void load_config_string(const char* str, const char* source_filename = nullptr); - // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. // Load settings into the provided settings instance. diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index eef4017c1..b31e59cc0 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -108,7 +108,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} ); - Refresh(this); + Refresh(true); // erase background } void RammingPanel::line_parameters_changed() { diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 8d7498170..52b6a01e9 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -9,6 +9,9 @@ #include #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" +#include "Model.hpp" + +wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, const std::string& icon, wxEvtHandler* event_handler) @@ -400,10 +403,9 @@ bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;//Slic3r::GUI::get_category_icon(); + std::map& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; for (auto& cat : m_opt_categories) m_name += cat + "; "; @@ -453,29 +455,34 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, const wxString &name, - const wxBitmap& icon, + const int volume_type, const int extruder/* = 0*/, const bool create_frst_child/* = true*/) { PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); if (!root) return wxDataViewItem(0); - const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); + wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); + + // because of istance_root is a last item of the object + int insert_position = root->GetChildCount() - 1; + if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot) + insert_position = -1; if (create_frst_child && root->m_volumes_cnt == 0) { - const auto bmp_solid_mesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); - const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, bmp_solid_mesh, extruder_str, 0); - root->Append(node); + const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0); + insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); root->m_volumes_cnt++; + if (insert_position > 0) insert_position++; } - const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, root->m_volumes_cnt); - root->Append(node); + const auto node = new PrusaObjectDataViewModelNode(root, name, *m_volume_bmps[volume_type], extruder_str, root->m_volumes_cnt); + insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); @@ -499,15 +506,13 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) { - int inst_root_id = -1; - int stop_search_i = parent_node->GetChildCount(); - if (stop_search_i > 2) stop_search_i = 2; - for (int i = 0; i < stop_search_i; ++i) - if (parent_node->GetNthChild(i)->m_type & itInstanceRoot) { - inst_root_id = i; - break; - } - return inst_root_id; + // because of istance_root is a last item of the object + const int inst_root_idx = parent_node->GetChildCount()-1; + + if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->m_type == itInstanceRoot) + return inst_root_idx; + + return -1; } wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) @@ -524,8 +529,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem & const wxDataViewItem inst_root_item((void*)inst_root_node); if (inst_root_id < 0) { - const unsigned insert_pos = parent_node->GetChildCount() == 0 || parent_node->GetNthChild(0)->m_type != itSettings ? 0 : 1; - parent_node->Insert(inst_root_node, insert_pos); + parent_node->Append(inst_root_node); // notify control ItemAdded(parent_item, inst_root_item); if (num == 1) num++; @@ -563,6 +567,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) auto id = node_parent->GetChildren().Index(node); auto idx = node->GetIdx(); node_parent->GetChildren().Remove(node); + + if (node->m_type == itVolume) + node_parent->m_volumes_cnt--; + if (id > 0) { if(id == node_parent->GetChildCount()) id--; ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); @@ -692,6 +700,9 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) auto item = wxDataViewItem(node); children.RemoveAt(id); + if (node->m_type == itVolume) + root->m_volumes_cnt--; + // free the node delete node; @@ -705,6 +716,39 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) #endif //__WXGTK__ } +void PrusaObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) +{ + PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!root) // happens if item.IsOk()==false + return; + + // first remove the node from the parent's array of children; + // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ + // thus removing the node from it doesn't result in freeing it + auto& children = root->GetChildren(); + for (int id = root->GetChildCount() - 1; id >= 0; --id) + { + auto node = children[id]; + if (node->m_type != itVolume) + continue; + + auto item = wxDataViewItem(node); + children.RemoveAt(id); + root->m_volumes_cnt--; + + // free the node + delete node; + + // notify control + ItemDeleted(parent, item); + } + + // set m_containet to FALSE if parent has no child +#ifndef __WXGTK__ + root->m_container = false; +#endif //__WXGTK__ +} + wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) { if (obj_idx >= m_objects.size()) @@ -718,7 +762,7 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) { - if (obj_idx >= m_objects.size()) { + if (obj_idx >= m_objects.size() || obj_idx < 0) { printf("Error! Out of objects range.\n"); return wxDataViewItem(0); } @@ -740,6 +784,25 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volu return wxDataViewItem(0); } +wxDataViewItem PrusaObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) +{ + if (obj_idx >= m_objects.size() || obj_idx < 0) { + printf("Error! Out of objects range.\n"); + return wxDataViewItem(0); + } + + auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx])); + if (!instances_item) + return wxDataViewItem(0); + + auto parent = (PrusaObjectDataViewModelNode*)instances_item.GetID();; + for (size_t i = 0; i < parent->GetChildCount(); i++) + if (parent->GetNthChild(i)->m_idx == inst_idx) + return wxDataViewItem(parent->GetNthChild(i)); + + return wxDataViewItem(0); +} + int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) { wxASSERT(item.IsOk()); @@ -1015,21 +1078,33 @@ ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const return node->m_type; } -wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const +wxDataViewItem PrusaObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const { - if (!item.IsOk()) + if (!parent_item.IsOk()) return wxDataViewItem(0); - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); if (node->GetChildCount() == 0) return wxDataViewItem(0); - if (node->GetNthChild(0)->m_type == itSettings) - return wxDataViewItem((void*)node->GetNthChild(0)); + for (int i = 0; i < node->GetChildCount(); i++) { + if (node->GetNthChild(i)->m_type == type) + return wxDataViewItem((void*)node->GetNthChild(i)); + } return wxDataViewItem(0); } +wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itSettings); +} + +wxDataViewItem PrusaObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const +{ + return GetItemByType(item, itInstanceRoot); +} + bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const { if (!item.IsOk()) @@ -1050,6 +1125,16 @@ void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, ItemChanged(item); } +void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const int type) +{ + if (!item.IsOk() || GetItemType(item) != itVolume) + return; + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + node->SetBitmap(*m_volume_bmps[type]); + ItemChanged(item); +} + //----------------------------------------------------------------------------- // PrusaDataViewBitmapText //----------------------------------------------------------------------------- @@ -1106,7 +1191,6 @@ wxSize PrusaBitmapTextRenderer::GetSize() const // ---------------------------------------------------------------------------- // PrusaDoubleSlider // ---------------------------------------------------------------------------- - PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, wxWindowID id, int lowerValue, @@ -1288,6 +1372,34 @@ double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) cons return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; } +std::vector PrusaDoubleSlider::GetTicksValues() const +{ + std::vector values; + + if (!m_values.empty()) + for (auto tick : m_ticks) + values.push_back(m_values[tick].second); + + return values; +} + +void PrusaDoubleSlider::SetTicksValues(const std::vector& heights) +{ + if (m_values.empty()) + return; + + m_ticks.clear(); + unsigned int i = 0; + for (auto h : heights) { + while (i < m_values.size() && m_values[i].second - 1e-6 < h) + ++i; + if (i == m_values.size()) + return; + m_ticks.insert(i); + } + +} + void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) { const double step = get_scroll_step(); @@ -1712,10 +1824,13 @@ void PrusaDoubleSlider::action_tick(const TicksAction action) m_ticks.insert(tick); else if (it != m_ticks.end() && action == taDel) m_ticks.erase(tick); - else + else { + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); return; + } } + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); Refresh(); Update(); } diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 60bc083fe..e5e4600da 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -424,7 +424,9 @@ private: class PrusaObjectDataViewModel :public wxDataViewModel { - std::vector m_objects; + std::vector m_objects; + std::vector m_volume_bmps; + public: PrusaObjectDataViewModel(); ~PrusaObjectDataViewModel(); @@ -432,7 +434,7 @@ public: wxDataViewItem Add(const wxString &name); wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item, const wxString &name, - const wxBitmap& icon, + const int volume_type, const int extruder = 0, const bool create_frst_child = true); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); @@ -441,8 +443,10 @@ public: wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); void DeleteAll(); void DeleteChildren(wxDataViewItem& parent); + void DeleteVolumeChildren(wxDataViewItem& parent); wxDataViewItem GetItemById(int obj_idx); wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); + wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); int GetIdByItem(const wxDataViewItem& item); int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; int GetVolumeIdByItem(const wxDataViewItem& item) const; @@ -488,9 +492,14 @@ public: virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } ItemType GetItemType(const wxDataViewItem &item) const ; + wxDataViewItem GetItemByType(const wxDataViewItem &parent_item, ItemType type) const; wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; + wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; bool IsSettingsItem(const wxDataViewItem &item) const; void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector& categories); + + void SetVolumeBitmaps(const std::vector& volume_bmps) { m_volume_bmps = volume_bmps; } + void SetVolumeType(const wxDataViewItem &item, const int type); }; // ---------------------------------------------------------------------------- @@ -613,6 +622,9 @@ private: // PrusaDoubleSlider // ---------------------------------------------------------------------------- +// custom message the slider sends to its parent to notify a tick-change: +wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + enum SelectedSlider { ssUndef, ssLower, @@ -623,6 +635,7 @@ enum TicksAction{ taAdd, taDel }; + class PrusaDoubleSlider : public wxControl { public: @@ -660,6 +673,8 @@ public: m_values = values; } void ChangeOneLayerLock(); + std::vector GetTicksValues() const; + void SetTicksValues(const std::vector& heights); void OnPaint(wxPaintEvent& ) { render();} void OnLeftDown(wxMouseEvent& event); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 180e052ff..11fa7e920 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -32,7 +32,7 @@ %name{_add_object} Ref add_object(); Ref _add_object_clone(ModelObject* other, bool copy_volumes = true) - %code%{ RETVAL = THIS->add_object(*other, copy_volumes); %}; + %code%{ auto ptr = THIS->add_object(*other); if (! copy_volumes) ptr->clear_volumes(); RETVAL = ptr; %}; void delete_object(size_t idx); void clear_objects(); size_t objects_count() @@ -286,8 +286,6 @@ ModelMaterial::attributes() %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %}; size_t split(unsigned int max_extruders); - - ModelMaterial* assign_unique_material(); }; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index b34444b23..cf3b67931 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -44,8 +44,6 @@ _constant() int region_count() %code%{ RETVAL = THIS->print()->regions().size(); %}; - int region_volumes_count() - %code%{ RETVAL = THIS->region_volumes.size(); %}; Ref print(); Ref model_object(); Ref config()