Cut WIP: Suppress un-universal scaling for cut objects

Added editing of the tolerance
This commit is contained in:
YuSanka 2022-07-21 17:04:37 +02:00
parent 003acee218
commit 0fd29dfec7
7 changed files with 155 additions and 72 deletions

View File

@ -713,7 +713,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t
ModelVolume* v = new ModelVolume(this, other);
if (type != ModelVolumeType::INVALID && v->type() != type)
v->set_type(type);
v->source.is_connector = other.source.is_connector;
v->cut_info = other.cut_info;
this->volumes.push_back(v);
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
// v->center_geometry_after_creation();
@ -1380,10 +1380,9 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn
void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes)
{
// discard old connector markers vor volumes
for (ModelVolume* volume : volumes) {
volume->source.is_connector = false;
}
// discard old connector markers for volumes
for (ModelVolume* volume : volumes)
volume->cut_info.discard();
if (cut_connectors.empty())
return;
@ -1404,8 +1403,8 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr
Vec3d::Ones()
));
new_volume->cut_info = { true, connector.radius_tolerance, connector.height_tolerance };
new_volume->name = name + "-" + std::to_string(++connector_id);
new_volume->source.is_connector = true;
}
cut_id.increase_connectors_cnt(cut_connectors.size());
@ -1481,27 +1480,47 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
dowels->input_file.clear();
}
using namespace Geometry;
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = Geometry::assemble_transform(
const auto instance_matrix = assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)),
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
const auto cut_matrix = Geometry::assemble_transform(-cut_center);
const auto invert_cut_matrix = Geometry::assemble_transform(cut_center, cut_rotation);
const auto cut_matrix = rotation_transform(cut_rotation).inverse() * assemble_transform(-cut_center);
const auto invert_cut_matrix = assemble_transform(cut_center, cut_rotation);
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX();
auto apply_tolerance = [](ModelVolume * vol)
{
Vec3d sf = vol->get_scaling_factor();
/*
// correct Z offset in respect to the new size
Vec3d pos = vol->get_offset();
pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance;
vol->set_offset(pos);
*/
// make a "hole" wider
sf[X] *= (1 + vol->cut_info.radius_tolerance);
sf[Y] *= (1 + vol->cut_info.radius_tolerance);
// make a "hole" dipper
sf[Z] *= (1 + vol->cut_info.height_tolerance);
vol->set_scaling_factor(sf);
};
for (ModelVolume* volume : volumes) {
const auto volume_matrix = volume->get_matrix();
@ -1510,43 +1529,33 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
volume->mmu_segmentation_facets.reset();
if (!volume->is_model_part()) {
if (volume->source.is_connector) {
if (volume->cut_info.is_connector) {
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper) ?
Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix :
instance_matrix * volume_matrix;
volume->set_transformation(m);
ModelVolume* vol = upper->add_volume(*volume);
// make a "hole" dipper
vol->set_scaling_factor(Z, 1.1 * vol->get_scaling_factor(Z));
vol->set_transformation(volume_matrix);
apply_tolerance(vol);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ?
Geometry::rotation_transform(Geometry::deg2rad(180.0) * Vec3d::UnitX()) * Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix :
instance_matrix * volume_matrix;
volume->set_transformation(m);
ModelVolume* vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
if (attributes.has(ModelObjectCutAttribute::CreateDowels))
// make a "hole" dipper
vol->set_scaling_factor(Z, 1.2 * vol->get_scaling_factor(Z));
apply_tolerance(vol);
else
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
// add one more solid part same as connector if this connector is a dowel
// But discard rotation and Z-offset for this volume
volume->set_rotation(Vec3d::Zero());
Vec3d offset = volume->get_offset();
offset[Z] = 0.0;
volume->set_offset(offset);
ModelVolume* vol = dowels->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
}
@ -1556,19 +1565,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
// #ysFIXME - add logic for the negative volumes/connectors
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower->add_volume(*volume);
}
}
else if (!volume->mesh().empty()
// && !volume->source.is_connector // we don't allow to cut a connectors
) {
else if (!volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix, true);
mesh.transform(cut_matrix * instance_matrix* volume_matrix, true);
volume->reset_mesh();
// Reset volume transformation except for offset
@ -1588,8 +1596,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) {
if (!attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper))
upper_mesh.transform(invert_cut_matrix);
upper_mesh.transform(invert_cut_matrix);
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
@ -1600,10 +1607,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
vol->set_material(volume->material_id(), *volume->material());
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower))
lower_mesh.transform(Geometry::assemble_transform(Vec3d::Zero(), Geometry::deg2rad(180.0)*Vec3d::UnitX()));
else
lower_mesh.transform(invert_cut_matrix);
lower_mesh.transform(invert_cut_matrix);
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
@ -1643,7 +1647,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
obj_instance->set_transformation(Geometry::Transformation());
obj_instance->set_offset(offset + displace);
obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipUpper) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z));
Vec3d rotation = Vec3d::Zero();
if (attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) {
Transform3d trafo = rotation_transform(cut_rotation).inverse();
if (i != instance)
trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo;
rotation = Transformation(trafo).get_rotation();
}
else if (attributes.has(ModelObjectCutAttribute::FlipUpper)) {
rotation = rotate_z180;
if (i != instance)
rotation[Z] = rot_z;
}
else if (i != instance)
rotation[Z] = rot_z/* * Vec3d::UnitZ()*/;
obj_instance->set_rotation(rotation);
}
res.push_back(upper);
@ -1666,7 +1685,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
const double rot_z = obj_instance->get_rotation().z();
obj_instance->set_transformation(Geometry::Transformation());
obj_instance->set_offset(offset);
obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z));
Vec3d rotation = Vec3d::Zero();
if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) {
Transform3d trafo = rotation_transform(rotate_z180) * rotation_transform(cut_rotation).inverse();
if (i != instance)
trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo;
rotation = Transformation(trafo).get_rotation();
}
else if (attributes.has(ModelObjectCutAttribute::FlipLower)) {
rotation = rotate_z180;
if (i != instance)
rotation[Z] = rot_z;
}
else if (i != instance)
rotation[Z] = rot_z;
obj_instance->set_rotation(rotation);
}
res.push_back(lower);

View File

@ -225,18 +225,20 @@ struct CutConnector
Vec3d rotation;
float radius;
float height;
float radius_tolerance;// [0.f : 1.f]
float height_tolerance;// [0.f : 1.f]
bool failed = false;
CutConnector()
: pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f)
: pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f)
{}
CutConnector(Vec3d p, Vec3d rot, float r, float h, bool fl = false)
: pos(p), rotation(rot), radius(r), height(h), failed(fl)
CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, bool fl = false)
: pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), failed(fl)
{}
CutConnector(const CutConnector& rhs) :
CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.failed) {}
CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.failed) {}
bool operator==(const CutConnector& sp) const;
@ -251,7 +253,7 @@ struct CutConnector
*/
template<class Archive> inline void serialize(Archive& ar)
{
ar(pos, rotation, radius, height, failed);
ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, failed);
}
static constexpr size_t steps = 32;
@ -696,16 +698,25 @@ public:
bool is_converted_from_meters{ false };
bool is_from_builtin_objects{ false };
bool is_connector{ false };
template<class Archive> void serialize(Archive& ar) {
//FIXME Vojtech: Serialize / deserialize only if the Source is set.
// likely testing input_file or object_idx would be sufficient.
ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects, is_connector);
ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects);
}
};
Source source;
// struct used by cut command
// It contains information about connetors
struct CutInfo
{
bool is_connector {false};
float radius_tolerance;// [0.f : 1.f]
float height_tolerance;// [0.f : 1.f]
void discard() { is_connector = false; }
} cut_info;
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }

View File

@ -2499,6 +2499,7 @@ void ObjectList::part_selection_changed()
bool enable_manipulation {true};
bool disable_ss_manipulation {false};
bool disable_ununiform_scale {false};
const auto item = GetSelection();
@ -2543,6 +2544,7 @@ void ObjectList::part_selection_changed()
disable_ss_manipulation = true;
break;
}
disable_ununiform_scale = !cut_objects.empty();
}
}
}
@ -2649,8 +2651,11 @@ void ObjectList::part_selection_changed()
if (disable_ss_manipulation)
wxGetApp().obj_manipul()->DisableScale();
else
else {
wxGetApp().obj_manipul()->Enable(enable_manipulation);
if (disable_ununiform_scale)
wxGetApp().obj_manipul()->DisableUnuniformScale();
}
}
if (update_and_show_settings)

View File

@ -580,7 +580,11 @@ void ObjectManipulation::Enable(const bool enadle)
{
for (auto editor : m_editors)
editor->Enable(enadle);
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt })
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt
#if ENABLE_WORLD_COORDINATE
,m_reset_skew_button
#endif // ENABLE_WORLD_COORDINATE
})
win->Enable(enadle);
}
@ -588,10 +592,19 @@ void ObjectManipulation::DisableScale()
{
for (auto editor : m_editors)
editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true);
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_lock_bnt })
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_lock_bnt
#if ENABLE_WORLD_COORDINATE
,m_reset_skew_button
#endif // ENABLE_WORLD_COORDINATE
})
win->Enable(false);
}
void ObjectManipulation::DisableUnuniformScale()
{
m_lock_bnt->disable();
}
void ObjectManipulation::update_ui_from_settings()
{
if (m_imperial_units != (wxGetApp().app_config->get("use_inches") == "1")) {

View File

@ -201,6 +201,7 @@ public:
void Enable(const bool enadle = true);
void Disable() { Enable(false); }
void DisableScale();
void DisableUnuniformScale();
void update_ui_from_settings();
bool use_colors() { return m_use_colors; }

View File

@ -430,12 +430,12 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i
return old_val != value;
}
bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double& value_in)
bool GLGizmoCut3D::render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in)
{
ImGui::AlignTextToFramePadding();
m_imgui->text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width);
ImGui::PushItemWidth(m_control_width * 0.85f);
float value = (float)value_in;
if (m_imperial_units)
@ -443,15 +443,25 @@ bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double&
float old_val = value;
const BoundingBoxf3 bbox = bounding_box();
const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0);
m_imgui->slider_float(("##" + label).c_str(), &value, 1.0f, mean_size);
ImGui::SameLine();
m_imgui->text(m_imperial_units ? _L("in") : _L("mm"));
float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0);
float min_size = 1.f;
if (m_imperial_units) {
mean_size *= ObjectManipulation::mm_to_in;
min_size *= ObjectManipulation::mm_to_in;
}
std::string format = m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm");
m_imgui->slider_float(("##" + label).c_str(), &value, min_size, mean_size, format.c_str());
value_in = (double)(value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0));
return old_val != value;
ImGui::SameLine(m_label_width + m_control_width + 3);
ImGui::PushItemWidth(m_control_width * 0.3f);
float old_tolerance, tolerance = old_tolerance = (float)tolerance_in;
m_imgui->slider_float(("##tolerance_" + label).c_str(), &tolerance, 1.f, 20.f, "%.f %%", 1.f, true, _L("Tolerance"));
tolerance_in = (int)tolerance;
return old_val != value || old_tolerance != tolerance;
}
void GLGizmoCut3D::render_move_center_input(int axis)
@ -1253,11 +1263,11 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
if (m_imgui->button(" " + _L("Reset") + " ##connectors"))
reset_connectors();
m_imgui->disabled_end();
/*
m_imgui->text(_L("Mode"));
render_connect_mode_radio_button(CutConnectorMode::Auto);
render_connect_mode_radio_button(CutConnectorMode::Manual);
*/
m_imgui->text(_L("Type"));
render_connect_type_radio_button(CutConnectorType::Plug);
render_connect_type_radio_button(CutConnectorType::Dowel);
@ -1267,12 +1277,16 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id))
update_connector_shape();
if (render_slicer_double_input(_u8L("Depth ratio"), m_connector_depth_ratio))
for (auto& connector : connectors)
if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance))
for (auto& connector : connectors) {
connector.height = float(m_connector_depth_ratio);
if (render_slicer_double_input(_u8L("Size"), m_connector_size))
for (auto& connector : connectors)
connector.height_tolerance = 0.01f * m_connector_depth_ratio;
}
if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance))
for (auto& connector : connectors) {
connector.radius = float(m_connector_size * 0.5);
connector.radius_tolerance = 0.01f * m_connector_size_tolerance;
}
m_imgui->disabled_end();
@ -1710,7 +1724,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
//std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction);
connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio));
connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(),
float(m_connector_size * 0.5), float(m_connector_depth_ratio),
float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance));
update_model_object();
m_selected.push_back(false);
assert(m_selected.size() == connectors.size());

View File

@ -81,6 +81,9 @@ class GLGizmoCut3D : public GLGizmoBase
double m_connector_depth_ratio{ 3.0 };
double m_connector_size{ 2.5 };
int m_connector_depth_ratio_tolerance{ 10 };
int m_connector_size_tolerance{ 0 };
float m_label_width{ 150.0 };
float m_control_width{ 200.0 };
bool m_imperial_units{ false };
@ -174,7 +177,7 @@ private:
void set_center(const Vec3d& center);
bool render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slicer_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in);
void render_move_center_input(int axis);
void render_connect_mode_radio_button(CutConnectorMode mode);
bool render_revert_button(const std::string& label);