diff --git a/resources/icons/cut_connectors.svg b/resources/icons/cut_connectors.svg
new file mode 100644
index 000000000..8cd03aa06
--- /dev/null
+++ b/resources/icons/cut_connectors.svg
@@ -0,0 +1,26 @@
+
+
+
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index d4c81299f..79c54748a 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -1191,11 +1191,21 @@ size_t ModelObject::parts_count() const
return num;
}
+bool ModelObject::has_connectors() const
+{
+ assert(is_cut());
+ for (const ModelVolume* v : this->volumes)
+ if (v->cut_info.is_connector)
+ return true;
+
+ return false;
+}
+
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
- int sectorCount;
+ int sectorCount {1};
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
@@ -1238,7 +1248,7 @@ void ModelObject::apply_cut_connectors(const std::string& new_name)
new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m *
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast()));
- new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
+ new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
new_volume->name = new_name + "-" + std::to_string(++connector_id);
}
cut_id.increase_connectors_cnt(cut_connectors.size());
@@ -1298,7 +1308,8 @@ void ModelVolume::reset_extra_facets()
void ModelVolume::apply_tolerance()
{
- if (!cut_info.is_connector)
+ assert(cut_info.is_connector);
+ if (cut_info.is_processed)
return;
Vec3d sf = get_scaling_factor();
@@ -1321,7 +1332,8 @@ void ModelVolume::apply_tolerance()
void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector& dowels, Vec3d& local_dowels_displace)
{
- volume->cut_info.discard();
+ assert(volume->cut_info.is_connector);
+ volume->cut_info.set_processed();
const auto volume_matrix = volume->get_matrix();
@@ -1546,10 +1558,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
volume->reset_extra_facets();
if (!volume->is_model_part()) {
- if (volume->cut_info.is_connector)
- process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
- else
+ if (volume->cut_info.is_processed)
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
+ else
+ process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
}
else if (!volume->mesh().empty())
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace);
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 16142ec05..cc2c612fe 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -477,6 +477,7 @@ public:
int get_repaired_errors_count(const int vol_idx = -1) const;
bool is_cut() const { return cut_id.id().valid(); }
+ bool has_connectors() const;
private:
friend class Model;
@@ -723,14 +724,26 @@ public:
struct CutInfo
{
bool is_connector{ false };
+ bool is_processed{ true };
CutConnectorType connector_type{ CutConnectorType::Plug };
- float radius_tolerance;// [0.f : 1.f]
- float height_tolerance;// [0.f : 1.f]
+ float radius_tolerance{ 0.f };// [0.f : 1.f]
+ float height_tolerance{ 0.f };// [0.f : 1.f]
- void discard() { is_connector = false; }
+ CutInfo() = default;
+ CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance) :
+ is_connector(true),
+ is_processed(false),
+ connector_type(type),
+ radius_tolerance(rad_tolerance),
+ height_tolerance(h_tolerance)
+ {}
+
+ void set_processed() { is_processed = true; }
};
CutInfo cut_info;
+ bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
+
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
#if ENABLE_RAYCAST_PICKING
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 6227c5130..05eb02778 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -1881,13 +1881,8 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
mv->seam_facets.reset();
break;
- case InfoItemType::Cut:
- if (0) { // #ysFIXME_Cut
- cnv->get_gizmos_manager().reset_all_states();
- Plater::TakeSnapshot(plater, _L("Remove cut connectors"));
- (*m_objects)[obj_idx]->cut_connectors.clear();
- } else
- Slic3r::GUI::show_error(nullptr, _L("Connectors cannot be deleted from cut object."));
+ case InfoItemType::CutConnectors:
+ show_error(nullptr, _L("Connectors cannot be deleted from cut object."));
break;
case InfoItemType::MmuSegmentation:
@@ -2422,9 +2417,12 @@ bool ObjectList::is_splittable(bool to_objects)
auto obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
- if ((*m_objects)[obj_idx]->volumes.size() > 1)
+ const ModelObject* object = (*m_objects)[obj_idx];
+ if (object->is_cut())
+ return false;
+ if (object->volumes.size() > 1)
return true;
- return (*m_objects)[obj_idx]->volumes[0]->is_splittable();
+ return object->volumes[0]->is_splittable();
}
return false;
}
@@ -2595,19 +2593,13 @@ void ObjectList::part_selection_changed()
}
case InfoItemType::CustomSupports:
case InfoItemType::CustomSeam:
-// case InfoItemType::Cut:
case InfoItemType::MmuSegmentation:
{
GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports :
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
- info_type == InfoItemType::Cut ? GLGizmosManager::EType::Cut :
GLGizmosManager::EType::MmuSegmentation;
if (gizmos_mgr.get_current_type() != gizmo_type)
gizmos_mgr.open_gizmo(gizmo_type);
- if (info_type == InfoItemType::Cut) {
- GLGizmoCut3D* cut = dynamic_cast(gizmos_mgr.get_current());
- cut->set_connectors_editing();
- }
break;
}
case InfoItemType::Sinking: { break; }
@@ -2762,7 +2754,7 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
for (InfoItemType type : {InfoItemType::CustomSupports,
InfoItemType::CustomSeam,
- InfoItemType::Cut,
+ InfoItemType::CutConnectors,
InfoItemType::MmuSegmentation,
InfoItemType::Sinking,
InfoItemType::VariableLayerHeight}) {
@@ -2783,11 +2775,8 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
});
break;
- case InfoItemType::Cut :
- if (0) // #ysFIXME_Cut
- should_show = !model_object->cut_connectors.empty();
- else
- should_show = model_object->is_cut() && model_object->volumes.size() > 1;
+ case InfoItemType::CutConnectors:
+ should_show = model_object->is_cut() && model_object->has_connectors() && model_object->volumes.size() > 1;
break;
case InfoItemType::VariableLayerHeight :
should_show = printer_technology() == ptFFF
@@ -2841,9 +2830,20 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
update_info_items(obj_idx, nullptr, call_selection_changed);
+ bool can_add_volumes = model_object->volumes.size() > 1;
+ if (can_add_volumes && model_object->is_cut()) {
+ int no_connectors_cnt = 0;
+ for (const ModelVolume* v : model_object->volumes)
+ if (!v->is_cut_connector())
+ no_connectors_cnt++;
+ can_add_volumes = no_connectors_cnt > 1;
+ }
+
// add volumes to the object
- if (model_object->volumes.size() > 1 && !model_object->is_cut()) {
+ if (can_add_volumes) {
for (const ModelVolume* volume : model_object->volumes) {
+ if (model_object->is_cut() && volume->is_cut_connector())
+ continue;
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item,
from_u8(volume->name),
volume->type(),
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 1260ee3bb..97714ea62 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -18,9 +18,17 @@
namespace Slic3r {
namespace GUI {
-static const double Margin = 20.0;
static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW();
+// connector colors
+static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
+static const ColorRGBA DOWEL_COLOR = ColorRGBA::DARK_YELLOW();
+static const ColorRGBA HOVERED_PLAG_COLOR = ColorRGBA::CYAN();
+static const ColorRGBA HOVERED_DOWEL_COLOR = ColorRGBA(0.0f, 0.5f, 0.5f, 1.0f);
+static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY();
+static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::DARK_GRAY();
+static const ColorRGBA CONNECTOR_DEF_COLOR = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f);
+
const unsigned int AngleResolution = 64;
const unsigned int ScaleStepsCount = 72;
const float ScaleStepRad = 2.0f * float(PI) / ScaleStepsCount;
@@ -1426,8 +1434,6 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
unselect_all_connectors();
set_connectors_editing(false);
}
-
- m_parent.request_extra_frame();
}
void GLGizmoCut3D::render_build_size()
@@ -1455,10 +1461,22 @@ void GLGizmoCut3D::reset_cut_plane()
update_clipper();
}
+void GLGizmoCut3D::invalidate_cut_plane()
+{
+ m_rotation_m = Transform3d::Identity();
+ m_plane_center = Vec3d::Zero();
+ m_min_pos = Vec3d::Zero();
+ m_max_pos = Vec3d::Zero();
+ m_bb_center = Vec3d::Zero();
+ m_center_offset = Vec3d::Zero();
+}
+
void GLGizmoCut3D::set_connectors_editing(bool connectors_editing)
{
m_connectors_editing = connectors_editing;
update_raycasters_for_picking();
+
+ m_parent.request_extra_frame();
}
void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
@@ -1661,13 +1679,14 @@ void GLGizmoCut3D::render_connectors()
pos[Z] += sla_shift;
// First decide about the color of the point.
- if (size_t(m_hover_id- m_connectors_group_id) == i)
- render_color = ColorRGBA::CYAN();
+ if (!m_connectors_editing)
+ render_color = CONNECTOR_DEF_COLOR;
+ else if (size_t(m_hover_id - m_connectors_group_id) == i)
+ render_color = connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR;
else if (m_selected[i])
- render_color = ColorRGBA::DARK_GRAY();
+ render_color = connector.attribs.type == CutConnectorType::Dowel ? SELECTED_DOWEL_COLOR : SELECTED_PLAG_COLOR;
else // neither hover nor picking
- render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f);
-
+ render_color = connector.attribs.type == CutConnectorType::Dowel ? DOWEL_COLOR : PLAG_COLOR;
// ! #ysFIXME rework get_volume_transformation
if (0) { // else { // neither hover nor picking
int mesh_id = -1;
@@ -1773,6 +1792,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
else {
// the object is SLA-elevated and the plane is under it.
}
+
+ invalidate_cut_plane();
}
@@ -2005,7 +2026,8 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
if (action == SLAGizmoEventType::LeftDown && !shift_down) {
// If there is no selection and no hovering, add new point
if (m_hover_id == -1 && !control_down && !alt_down)
- return add_connector(connectors, mouse_position);
+ if (!add_connector(connectors, mouse_position))
+ unselect_all_connectors();
return true;
}
if (!m_connectors_editing)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
index 2df337b04..fe8422865 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
@@ -149,6 +149,7 @@ public:
void update_clipper();
void update_clipper_on_render();
void set_connectors_editing() { m_connectors_editing = true; }
+ void invalidate_cut_plane();
BoundingBoxf3 bounding_box() const;
BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const;
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 16433bc79..594e0716c 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -1228,7 +1228,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty
case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break;
case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break;
case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break;
- case InfoItemType::Cut: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break;
+ case InfoItemType::CutConnectors: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break;
default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break;
}
}
diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp
index 61d5bf6ec..4c592b878 100644
--- a/src/slic3r/GUI/ObjectDataViewModel.cpp
+++ b/src/slic3r/GUI/ObjectDataViewModel.cpp
@@ -38,7 +38,7 @@ static constexpr char LayerRootIcon[] = "edit_layers_all";
static constexpr char LayerIcon[] = "edit_layers_some";
static constexpr char WarningIcon[] = "exclamation";
static constexpr char WarningManifoldIcon[] = "exclamation_manifold";
-static constexpr char LockIcon[] = "lock_closed";
+static constexpr char LockIcon[] = "cut_";
struct InfoItemAtributes {
std::string name;
@@ -49,7 +49,7 @@ const std::map INFO_ITEMS{
// info_item Type info_item Name info_item BitmapName
{ InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports_" }, },
{ InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, },
- { InfoItemType::Cut, {L("Cut connectors"), "cut_" }, },
+ { InfoItemType::CutConnectors, {L("Cut connectors"), "cut_connectors" }, },
{ InfoItemType::MmuSegmentation, {L("Multimaterial painting"), "mmu_segmentation_"}, },
{ InfoItemType::Sinking, {L("Sinking"), "sinking"}, },
{ InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers"}, },
diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp
index 87be7a4e2..bc9144803 100644
--- a/src/slic3r/GUI/ObjectDataViewModel.hpp
+++ b/src/slic3r/GUI/ObjectDataViewModel.hpp
@@ -51,7 +51,7 @@ enum class InfoItemType
Undef,
CustomSupports,
CustomSeam,
- Cut,
+ CutConnectors,
MmuSegmentation,
Sinking,
VariableLayerHeight
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index d09de9cf3..bb3f6e41a 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -97,6 +97,7 @@
#include "MsgDialog.hpp"
#include "ProjectDirtyStateManager.hpp"
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
+#include "Gizmos/GLGizmoCut.hpp"
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
@@ -2980,6 +2981,9 @@ void Plater::priv::object_list_changed()
const bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() == ModelInstancePVS_Inside;
sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits);
+
+ // invalidate CutGizmo after changes in ObjectList
+ static_cast(q->canvas3D()->get_gizmos_manager().get_gizmo(GLGizmosManager::Cut))->invalidate_cut_plane();
}
void Plater::priv::select_all()