* Suppress to split cut objects
* ObjectList:
  * Use another icons to mark the cut objects and connectors 
  * For the cut object show parts, which are not connectors
* Set different colors for the Plugs and Dowels
* CutGizmo:
  * Invalidate CutGizmo after changes in ObjectList or perform a cut
  * CupPlane in Connectors mode: Unselect selection, when click on empty space
  * Connectors mode: Fixed performance issue
This commit is contained in:
YuSanka 2022-09-27 13:54:44 +02:00
parent e689be65db
commit 0201a5055a
10 changed files with 123 additions and 45 deletions

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="add_x5F_part">
<g>
<path fill="#ED6B21" d="M14.62,4.37c-0.01-0.14,0.06-0.34,0.15-0.44l0.13-0.15c0.09-0.11,0.12-0.3,0.07-0.43l-0.2-0.49
c-0.05-0.13-0.21-0.24-0.35-0.25l-0.2-0.01c-0.14-0.01-0.33-0.1-0.42-0.21c-0.09-0.1-0.37-0.46-0.38-0.6l-0.01-0.2
c-0.01-0.14-0.12-0.3-0.25-0.35l-0.49-0.2C12.52,0.97,12.33,1,12.22,1.1l-0.15,0.13c-0.11,0.09-0.31,0.16-0.44,0.15
c-0.14-0.01-0.59-0.06-0.69-0.15L10.78,1.1c-0.11-0.09-0.3-0.12-0.43-0.07l-0.49,0.2C9.73,1.28,9.61,1.44,9.6,1.58l-0.01,0.2
C9.58,1.92,9.49,2.11,9.38,2.2c-0.1,0.09-0.46,0.37-0.6,0.38L8.58,2.6c-0.14,0.01-0.3,0.12-0.35,0.25l-0.2,0.49
C7.97,3.48,8,3.67,8.1,3.78l0.13,0.15c0.09,0.11,0.16,0.31,0.15,0.44C8.37,4.52,8.32,4.96,8.23,5.07L8.1,5.22
C8,5.33,7.97,5.52,8.03,5.65l0.2,0.49C8.28,6.27,8.44,6.39,8.58,6.4l0.2,0.01c0.14,0.01,0.33,0.1,0.42,0.21
c0.09,0.1,0.37,0.46,0.38,0.6l0.01,0.2c0.01,0.14,0.12,0.3,0.25,0.35l0.49,0.2C10.48,8.03,10.67,8,10.78,7.9l0.15-0.13
c0.11-0.09,0.31-0.16,0.44-0.15c0.14,0.01,0.59,0.06,0.69,0.15l0.15,0.13c0.11,0.09,0.3,0.12,0.43,0.07l0.49-0.2
c0.13-0.05,0.24-0.21,0.25-0.35l0.01-0.2c0.01-0.14,0.1-0.33,0.21-0.42s0.46-0.37,0.6-0.38l0.2-0.01c0.14-0.01,0.3-0.12,0.35-0.25
l0.2-0.49C15.03,5.52,15,5.33,14.9,5.22l-0.13-0.15C14.68,4.96,14.63,4.51,14.62,4.37z M11.5,6.6c-1.16,0-2.1-0.94-2.1-2.1
s0.94-2.1,2.1-2.1s2.1,0.94,2.1,2.1S12.66,6.6,11.5,6.6z"/>
</g>
<path fill="#808080" d="M10.98,9.78c-0.29,0-0.52,0.23-0.52,0.52v2.09v1.04c0,0.29-0.23,0.52-0.52,0.52H2.62
c-0.29,0-0.53-0.24-0.53-0.53L2.04,6.12c0-0.14,0.05-0.27,0.15-0.37c0.1-0.1,0.23-0.15,0.37-0.15l3.19,0v0
c0.29,0,0.52-0.23,0.52-0.52S6.04,4.55,5.75,4.55H3.66c-0.01,0-0.01,0-0.02,0l-1.08,0c-0.42,0-0.81,0.16-1.11,0.46
C1.16,5.31,1,5.71,1,6.13l0.04,7.31C1.05,14.3,1.75,15,2.62,15h7.31c0.86,0,1.57-0.7,1.57-1.57v-1.04V10.3
C11.5,10.01,11.27,9.78,10.98,9.78z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -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<double>()));
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<ModelObject*>& 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);

View File

@ -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

View File

@ -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<GLGizmoCut3D*>(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(),

View File

@ -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)

View File

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

View File

@ -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;
}
}

View File

@ -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<InfoItemType, InfoItemAtributes> 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"}, },

View File

@ -51,7 +51,7 @@ enum class InfoItemType
Undef,
CustomSupports,
CustomSeam,
Cut,
CutConnectors,
MmuSegmentation,
Sinking,
VariableLayerHeight

View File

@ -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<GLGizmoCut3D*>(q->canvas3D()->get_gizmos_manager().get_gizmo(GLGizmosManager::Cut))->invalidate_cut_plane();
}
void Plater::priv::select_all()