diff --git a/resources/icons/add_seam.svg b/resources/icons/add_seam.svg new file mode 100644 index 000000000..ef6a831b7 --- /dev/null +++ b/resources/icons/add_seam.svg @@ -0,0 +1,75 @@ + + + +image/svg+xml + + \ No newline at end of file diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0bd284a24..156654b63 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3120,7 +3120,7 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptrlast_pos(); if (m_config.spiral_vase) { loop.split_at(last_pos, false); - } else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear || seam_position == spHidden) { + } else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear || seam_position == spHidden || seam_position == spCustom) { Polygon polygon = loop.polygon(); const coordf_t nozzle_dmr = EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, loop.paths.front().width); const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5); @@ -3128,6 +3128,33 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptrobject()->bounding_box().center(); + // Look for all lambda-seam-modifiers below current z, choose the highest one + ModelVolume *v_lambda_seam = nullptr; + Vec3d lambda_pos; + for (ModelVolume *v : m_layer->object()->model_object()->volumes) + if (v->is_seam_position()) { + Vec3d test_lambda_pos = m_layer->object()->model_object()->instances.front()->transform_vector(v->get_offset()); + //use this one if the first or nearer (in z) + if (v_lambda_seam == nullptr || std::abs(m_layer->print_z - test_lambda_pos.z()) < std::abs(m_layer->print_z - lambda_pos.z())) { + v_lambda_seam = v; + lambda_pos = m_layer->object()->model_object()->instances.front()->transform_vector(v_lambda_seam->get_offset()); + } + } + + if (v_lambda_seam != nullptr) { + lambda_pos = m_layer->object()->model_object()->instances.front()->transform_vector(v_lambda_seam->get_offset(), true); + // Found, get the center point and apply rotation and scaling of Model instance. Continues to spAligned if not found or Weight set to Zero. + last_pos = Point::new_scale(lambda_pos.x(), lambda_pos.y()); + // Weight is set by user and stored in the radius of the sphere + double weight_temp = (m_layer->object()->model_object()->instances.front()->transform_bounding_box(v_lambda_seam->mesh().bounding_box(), true).size().x() / 2.0); + last_pos_weight = std::max(0.0, std::round(100 * (weight_temp - 0.5))); + if (last_pos_weight <= 0.0) + seam_position = spHidden; + } + } if (seam_position == spAligned) { // Seam is aligned to the seam at the preceding layer. if (m_layer != NULL && m_seam_position.count(m_layer->object()) > 0) { @@ -3202,7 +3229,9 @@ void GCode::split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr ModelVolumeWithStatus; std::vector old_volumes; @@ -420,7 +420,7 @@ void Print::model_volume_list_update_supports(ModelObject &model_object_dst, con // For support modifiers, the type may have been switched from blocker to enforcer and vice versa. assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type()); model_object_dst.volumes.emplace_back(model_volume_dst); - if (model_volume_dst->is_support_modifier()) { + if (model_volume_dst->is_support_modifier() || model_volume_dst->is_seam_position()) { // For support modifiers, the type may have been switched from blocker to enforcer and vice versa. model_volume_dst->set_type(model_volume_src->type()); model_volume_dst->set_transformation(model_volume_src->get_transformation()); @@ -428,7 +428,7 @@ void Print::model_volume_list_update_supports(ModelObject &model_object_dst, con assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix())); } else { // The volume was not found in the old list. Create a new copy. - assert(model_volume_src->is_support_modifier()); + assert(model_volume_src->is_support_modifier() || model_volume_dst->is_seam_position()); model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src)); model_object_dst.volumes.back()->set_model_object(&model_object_dst); } @@ -895,7 +895,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER); bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER); bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); - if (model_parts_differ || modifiers_differ || + bool seam_position_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SEAM_POSITION); + if (model_parts_differ || modifiers_differ || model_object.origin_translation != model_object_new.origin_translation || model_object.layer_height_profile != model_object_new.layer_height_profile || ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) { @@ -916,7 +917,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (auto it = range.first; it != range.second; ++ it) update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); // Copy just the support volumes. - model_volume_list_update_supports(model_object, model_object_new); + model_volume_list_update_supports_seams(model_object, model_object_new); + }else if (seam_position_differ) { + // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. + this->call_cancel_callback(); + update_apply_status(false); + // Invalidate just the gcode step. + invalidate_step(psGCodeExport); + // Copy just the seam volumes. + model_volume_list_update_supports_seams(model_object, model_object_new); } if (! model_parts_differ && ! modifiers_differ) { // Synchronize Object's config. @@ -1051,11 +1060,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (PrintRegion *region : m_regions) region->m_refcnt = 0; for (PrintObject *print_object : m_objects) { - int idx_region = 0; - for (const auto &volumes : print_object->region_volumes) { - if (! volumes.empty()) - ++ m_regions[idx_region]->m_refcnt; - ++ idx_region; + for (int idx_region = 0; idx_region < print_object->region_volumes.size(); ++idx_region) { + if (!print_object->region_volumes[idx_region].empty()) + ++ m_regions[idx_region]->m_refcnt; } } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 8d753dc6f..a9cee4fb1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -488,7 +488,7 @@ private: void _make_wipe_tower(); // 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); + static void model_volume_list_update_supports_seams(ModelObject &model_object_dst, const ModelObject &model_object_src); PrintConfig m_config; PrintObjectConfig m_default_object_config; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 74aaa0fd0..ad158e7d3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2551,18 +2551,25 @@ void PrintConfigDef::init_fff_params() def = this->add("seam_position", coEnum); def->label = L("Seam position"); def->category = OptionCategory::perimeter; - def->tooltip = L("Position of perimeters starting points."); + def->tooltip = L("Position of perimeters starting points." + "\n --- When using Custom ---" + "\nYou have to create one or more seam sphere in the context menu of the object." + " Note that the custom setting is automatically added to the object when creating a seam object," + " so you shouldn't have to set it in the global config." + " The center of the seam sphere is used to position the seam. If you set multiple spheres, the nearest in z is chosen for a given layer."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("random"); def->enum_values.push_back("near"); def->enum_values.push_back("aligned"); def->enum_values.push_back("rear"); def->enum_values.push_back("hidden"); + def->enum_values.push_back("custom"); def->enum_labels.push_back(L("Random")); def->enum_labels.push_back(L("Nearest")); def->enum_labels.push_back(L("Aligned")); def->enum_labels.push_back(L("Rear")); def->enum_labels.push_back(L("Corners")); + def->enum_labels.push_back(L("Custom")); def->mode = comSimple; def->set_default_value(new ConfigOptionEnum(spHidden)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 69de0075a..1286cb92d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -67,7 +67,7 @@ enum SupportMaterialPattern { }; enum SeamPosition { - spRandom, spNearest, spAligned, spRear, spHidden + spRandom, spNearest, spAligned, spRear, spHidden, spCustom }; enum SLAMaterial { @@ -207,6 +207,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::ge keys_map["aligned"] = spAligned; keys_map["rear"] = spRear; keys_map["hidden"] = spHidden; + keys_map["custom"] = spCustom; } return keys_map; } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 228824a06..94982bb5a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -288,8 +288,8 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) { if (model_volume->is_modifier()) { color[0] = 0.2f; - color[1] = 1.0f; - color[2] = 0.2f; + color[1] = 0.2f; + color[2] = 1.0f; } else if (model_volume->is_support_blocker()) { color[0] = 1.0f; @@ -298,6 +298,11 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) } else if (model_volume->is_support_enforcer()) { color[0] = 0.2f; + color[1] = 1.0f; + color[2] = 0.2f; + } + else if (model_volume->is_seam_position()) { + color[0] = 0.9f; color[1] = 0.2f; color[2] = 1.0f; } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f5c06f84f..d96ec2ae4 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -51,7 +51,8 @@ static std::vector> ADD_VOLUME_MENU_ITEMS = {L("Add part"), "add_part" }, // ~ModelVolumeType::MODEL_PART {L("Add modifier"), "add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER {L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER - {L("Add support blocker"), "support_blocker"} // ~ModelVolumeType::SUPPORT_BLOCKER + {L("Add support blocker"), "support_blocker"}, // ~ModelVolumeType::SUPPORT_BLOCKER + {L("Add seam position"), "add_seam"} // ~ModelVolumeType::SEAM_POSITION }; static PrinterTechnology printer_technology() @@ -577,13 +578,15 @@ void ObjectList::init_icons() m_bmp_solidmesh = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART) ].second); m_bmp_modifiermesh = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::PARAMETER_MODIFIER)].second); m_bmp_support_enforcer = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER) ].second); - m_bmp_support_blocker = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER) ].second); + m_bmp_support_blocker = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER) ].second); + m_bmp_seam_position = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SEAM_POSITION) ].second); m_bmp_vector.reserve(4); // bitmaps for different types of parts - m_bmp_vector.push_back(&m_bmp_solidmesh.bmp()); - m_bmp_vector.push_back(&m_bmp_modifiermesh.bmp()); - m_bmp_vector.push_back(&m_bmp_support_enforcer.bmp()); - m_bmp_vector.push_back(&m_bmp_support_blocker.bmp()); + m_bmp_vector.push_back(&m_bmp_solidmesh.bmp()); + m_bmp_vector.push_back(&m_bmp_modifiermesh.bmp()); + m_bmp_vector.push_back(&m_bmp_support_enforcer.bmp()); + m_bmp_vector.push_back(&m_bmp_support_blocker.bmp()); + m_bmp_vector.push_back(&m_bmp_seam_position.bmp()); // Set volumes default bitmaps for the model @@ -605,7 +608,8 @@ void ObjectList::msw_rescale_icons() for (ScalableBitmap* bitmap : { &m_bmp_solidmesh, // Add part &m_bmp_modifiermesh, // Add modifier &m_bmp_support_enforcer, // Add support enforcer - &m_bmp_support_blocker }) // Add support blocker + &m_bmp_support_blocker, // Add support blocker + &m_bmp_seam_position }) // Add seam position { bitmap->msw_rescale(); m_bmp_vector.push_back(& bitmap->bmp()); @@ -1442,12 +1446,15 @@ wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeTy sub_menu->AppendSeparator(); } - for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) + std::vector items = { "Box", "Cylinder", "Sphere", "Slab" }; + if(type == ModelVolumeType::SEAM_POSITION) items = { "Sphere" }; + + for (std::string& item : items) { - if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0) + if (type == ModelVolumeType::INVALID && item == "Slab") continue; - append_menu_item(sub_menu, wxID_ANY, _(item), "", - [this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu); + append_menu_item(sub_menu, wxID_ANY, _(L(item)), "", + [this, type, item](wxCommandEvent&) { load_generic_subobject(L(item), type); }, "", menu); } return sub_menu; @@ -1481,6 +1488,10 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu) [this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_BLOCKER); }, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].second, nullptr, [this]() { return is_instance_or_object_selected(); }, parent); + append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SEAM_POSITION)].first), "", + [this](wxCommandEvent&) { load_generic_subobject(L("Sphere"), ModelVolumeType::SEAM_POSITION); }, + ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SEAM_POSITION)].second, nullptr, + [this]() { return is_instance_or_object_selected(); }, parent); return; } @@ -1488,10 +1499,16 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu) for (size_t type = (mode == comExpert || wxGetApp().app_config->get("objects_always_expert") == "1" ? 0 : 1) ; type < ADD_VOLUME_MENU_ITEMS.size(); type++) { auto& item = ADD_VOLUME_MENU_ITEMS[type]; - - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type)); - append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - [this]() { return is_instance_or_object_selected(); }, parent); + if (type == int(ModelVolumeType::SEAM_POSITION)) { + append_menu_item(menu, wxID_ANY, _(item.first), "", + [this](wxCommandEvent&) { load_generic_subobject(L("Sphere"), ModelVolumeType::SEAM_POSITION); }, + item.second, nullptr, + [this]() { return is_instance_or_object_selected(); }, parent); + } else { + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type)); + append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, + [this]() { return is_instance_or_object_selected(); }, parent); + } } } @@ -1969,7 +1986,7 @@ static TriangleMesh create_mesh(const std::string& type_name, const BoundingBoxf else if (type_name == "Cylinder") // Centered around 0, sitting on the print bed. // The cylinder has the same volume as the box above. - mesh = make_cylinder(0.564 * side, side); + mesh = make_cylinder(0.564 * side, bb.size().z()>0 ? bb.size().z() : side); else if (type_name == "Sphere") // Centered around 0, half the sphere below the print bed, half above. // The sphere has the same volume as the box above. @@ -2011,6 +2028,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode // Bounding box of the selected instance in world coordinate system including the translation, without modifiers. BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx); + if(type == ModelVolumeType::SEAM_POSITION) + model_object.config.set_key_value("seam_position", new ConfigOptionEnum(spCustom)); + TriangleMesh mesh = create_mesh(type_name, instance_bb); // Mesh will be centered when loading. @@ -2033,7 +2053,11 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset); } - const wxString name = _(L("Generic")) + "-" + _(type_name); + std::string base_name = "Generic"; + if (type == ModelVolumeType::SEAM_POSITION) base_name = "Seam"; + if (type == ModelVolumeType::SUPPORT_ENFORCER) base_name = "Support"; + if (type == ModelVolumeType::SUPPORT_BLOCKER) base_name = "Blocker"; + const wxString name = _(L(base_name)) + "-" + _(type_name); new_volume->name = into_u8(name); // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -2049,6 +2073,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode //#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); //#endif //no __WXOSX__ //__WXMSW__ + if (type == ModelVolumeType::SEAM_POSITION) + this->update_after_undo_redo(); } void ObjectList::load_shape_object(const std::string& type_name) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 7a7c38ba8..32e4160c2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -127,6 +127,7 @@ private: ScalableBitmap m_bmp_solidmesh; ScalableBitmap m_bmp_support_enforcer; ScalableBitmap m_bmp_support_blocker; + ScalableBitmap m_bmp_seam_position; ScalableBitmap m_bmp_manifold_warning; ScalableBitmap m_bmp_cog;