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 @@
+
+
+
+
\ 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;