WIP: Cut with Rivets

* Code refactoring: get_connector_mesh() and apply_cut_connectors() moved from ModelObject to CutGizmo.
* Allow to change values of space and bulges for snaps
This commit is contained in:
YuSanka 2023-07-12 18:06:51 +02:00
parent 2e6d1ff08f
commit 7cd99d98f5
6 changed files with 139 additions and 81 deletions

View File

@ -1280,66 +1280,6 @@ bool ModelObject::has_connectors() const
return false;
}
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
int sectorCount {1};
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
break;
case CutConnectorShape::Square:
sectorCount = 4;
break;
case CutConnectorShape::Circle:
sectorCount = 360;
break;
case CutConnectorShape::Hexagon:
sectorCount = 6;
break;
default:
break;
}
if (connector_attributes.type == CutConnectorType::Rivet)
connector_mesh = its_make_rivet(1.0, 1.0);
else if (connector_attributes.style == CutConnectorStyle::Prism)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
return connector_mesh;
}
void ModelObject::apply_cut_connectors(const std::string& new_name)
{
if (cut_connectors.empty())
return;
using namespace Geometry;
size_t connector_id = cut_id.connectors_cnt();
for (const CutConnector& connector : cut_connectors) {
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
// Mesh will be centered when loading.
ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
// Transform the new modifier to be aligned inside the instance
new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m *
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast<double>()));
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());
// delete all connectors
cut_connectors.clear();
}
void ModelObject::invalidate_cut()
{
this->cut_id.invalidate();

View File

@ -463,8 +463,6 @@ public:
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(const std::string& name);
// invalidate cut state for this object and its connectors/volumes
void invalidate_cut();
// delete volumes which are marked as connector for this object

View File

@ -1262,7 +1262,7 @@ indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorC
return mesh;
}
indexed_triangle_set its_make_rivet(double r, double h, float space_proportion)
indexed_triangle_set its_make_snap(double r, double h, float space_proportion, float bulge_proportion)
{
const float radius = (float)r;
const float height = (float)h;
@ -1272,7 +1272,7 @@ indexed_triangle_set its_make_rivet(double r, double h, float space_proportion)
const float space_len = space_proportion * radius;
const float b_len = radius;
const float m_len = (1 + 0.5f*space_proportion) * radius;
const float m_len = (1 + bulge_proportion) * radius;
const float t_len = 0.5f * radius;
const float b_height = 0.f;
@ -1351,7 +1351,7 @@ indexed_triangle_set its_make_rivet(double r, double h, float space_proportion)
}
// add d side vertices and facets
while (b_angle_start < b_angle_stop) {
while (!is_approx(b_angle_start, b_angle_stop)) {
b_angle_start += b_angle_step;
t_angle_start += t_angle_step;

View File

@ -321,7 +321,7 @@ indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);
indexed_triangle_set its_make_rivet(double r, double h, float space_proportion = 0.25f);
indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f);
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }

View File

@ -230,7 +230,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
{"Size" , _u8L("Size")},
};
update_connector_shape();
// update_connector_shape();
}
std::string GLGizmoCut3D::get_tooltip() const
@ -498,8 +498,8 @@ bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std:
ImGui::AlignTextToFramePadding();
const bool is_changed = m_imgui->combo(label, lines, selection_idx, 0, m_label_width, m_control_width);
if (is_changed)
update_connector_shape();
//if (is_changed)
// update_connector_shape();
return is_changed;
}
@ -524,7 +524,7 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i
return !is_approx(old_val, value);
}
bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float max_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/)
bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/)
{
constexpr float UndefMinVal = -0.1f;
const float f_mm_to_in = static_cast<float>(ObjectManipulation::mm_to_in);
@ -548,7 +548,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v
const BoundingBoxf3 bbox = m_bounding_box;
const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f);
const float max_v = max_val > 0.f ? /*std::min(max_val, mean_size)*/max_val : mean_size;
const float min_v = min_val > 0.f ? /*std::min(max_val, mean_size)*/min_val : 1.f;
ImGuiWrapper::text(label);
@ -556,7 +556,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v
ImGui::PushItemWidth(m_control_width * 0.7f);
// const bool is_value_changed = render_slider("##" + label, value_in, 1.f, mean_size, _L("Value"));
const bool is_value_changed = render_slider("##" + label, value_in, 1.f, max_v, _L("Value"));
const bool is_value_changed = render_slider("##" + label, value_in, min_v, mean_size, _L("Value"));
ImGui::SameLine();
ImGui::PushItemWidth(m_control_width * 0.45f);
@ -599,7 +599,7 @@ bool GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type)
ImGui::PushItemWidth(m_control_width);
if (ImGui::RadioButton(m_connector_types[size_t(type)].c_str(), m_connector_type == type)) {
m_connector_type = type;
update_connector_shape();
// update_connector_shape();
return true;
}
return false;
@ -2013,7 +2013,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
m_imgui->disabled_end();
if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance))
const float depth_min_value = m_connector_type == CutConnectorType::Rivet ? m_connector_size : -0.1f;
if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance, depth_min_value))
apply_selected_connectors([this, &connectors](size_t idx) {
if (m_connector_depth_ratio > 0)
connectors[idx].height = m_connector_depth_ratio;
@ -2029,6 +2030,45 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
connectors[idx].radius_tolerance = 0.5f * m_connector_size_tolerance;
});
if (m_connector_type == CutConnectorType::Rivet) {
const std::string format = "%.0f %%";
bool is_changed = false;
{
const std::string label = _u8L("Bulge");
ImGuiWrapper::text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width * 0.7f);
float val = m_snap_bulge_proportion *100.f;
if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, 5.f, 100.f * m_snap_space_proportion, format.c_str(), 1.f, true, _u8L("Bulge proportion related to radius"))) {
m_snap_bulge_proportion = val * 0.01f;
is_changed = true;
}
}
{
const std::string label = _u8L("Space");
ImGuiWrapper::text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width * 0.7f);
float val = m_snap_space_proportion *100.f;
if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, 10.f, 50.f, format.c_str(), 1.f, true, _u8L("Space proportion related to radius"))) {
m_snap_space_proportion = val * 0.01f;
is_changed = true;
}
}
if (is_changed) {
update_connector_shape();
update_raycasters_for_picking();
}
}
ImGui::Separator();
if (m_imgui->button(_L("Confirm connectors"))) {
@ -2224,7 +2264,7 @@ void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val,
reset_cut_by_contours();
// Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction);
}
};
}
void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
{
@ -2758,7 +2798,7 @@ void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, int &dowels_count)
connector.pos += m_cut_normal * 0.5 * double(connector.height);
}
}
mo->apply_cut_connectors(_u8L("Connector"));
apply_cut_connectors(mo, _u8L("Connector"));
}
}
@ -3252,7 +3292,7 @@ void GLGizmoCut3D::init_connector_shapes()
if (type == CutConnectorType::Rivet && shape != CutConnectorShape::Circle)
continue;
const CutConnectorAttributes attribs = { type, style, shape };
indexed_triangle_set its = ModelObject::get_connector_mesh(attribs);
indexed_triangle_set its = get_connector_mesh(attribs);
m_shapes[attribs].model.init_from(its);
m_shapes[attribs].mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
}
@ -3263,9 +3303,18 @@ void GLGizmoCut3D::update_connector_shape()
{
CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) };
const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs);
m_connector_mesh.clear();
m_connector_mesh = TriangleMesh(its);
if (m_connector_type == CutConnectorType::Rivet) {
indexed_triangle_set its = get_connector_mesh(attribs);
m_shapes[attribs].reset();
m_shapes[attribs].model.init_from(its);
m_shapes[attribs].mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
//const indexed_triangle_set its = get_connector_mesh(attribs);
//m_connector_mesh.clear();
//m_connector_mesh = TriangleMesh(its);
}
}
bool GLGizmoCut3D::cut_line_processing() const
@ -3522,5 +3571,69 @@ void GLGizmoCut3D::data_changed(bool is_serializing)
}
indexed_triangle_set GLGizmoCut3D::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
int sectorCount{ 1 };
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
break;
case CutConnectorShape::Square:
sectorCount = 4;
break;
case CutConnectorShape::Circle:
sectorCount = 360;
break;
case CutConnectorShape::Hexagon:
sectorCount = 6;
break;
default:
break;
}
if (connector_attributes.type == CutConnectorType::Rivet)
connector_mesh = its_make_snap(1.0, 1.0, m_snap_space_proportion, m_snap_bulge_proportion);
else if (connector_attributes.style == CutConnectorStyle::Prism)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
return connector_mesh;
}
void GLGizmoCut3D::apply_cut_connectors(ModelObject* mo, const std::string& connector_name)
{
if (mo->cut_connectors.empty())
return;
using namespace Geometry;
size_t connector_id = mo->cut_id.connectors_cnt();
for (const CutConnector& connector : mo->cut_connectors) {
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
// Mesh will be centered when loading.
ModelVolume* new_volume = mo->add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
// Transform the new modifier to be aligned inside the instance
new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m *
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast<double>()));
new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
new_volume->name = connector_name + "-" + std::to_string(++connector_id);
}
mo->cut_id.increase_connectors_cnt(mo->cut_connectors.size());
// delete all connectors
mo->cut_connectors.clear();
}
} // namespace GUI
} // namespace Slic3r

View File

@ -127,6 +127,10 @@ class GLGizmoCut3D : public GLGizmoBase
float m_groove_width_tolerance{ 0.1f };
bool m_optimaze_groove_rendering{ true };
// Input params for cut with snaps
float m_snap_bulge_proportion{ 0.15f };
float m_snap_space_proportion{ 0.3f };
bool m_hide_cut_plane{ false };
bool m_connectors_editing{ false };
bool m_cut_plane_as_circle{ false };
@ -323,7 +327,7 @@ private:
bool render_cut_mode_combo();
bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float max_val = -0.1f, float max_tolerance = -0.1f);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
void render_move_center_input(int axis);
void render_connect_mode_radio_button(CutConnectorMode mode);
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
@ -364,6 +368,9 @@ private:
void check_and_update_connectors_state();
void toggle_model_objects_visibility();
indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(ModelObject* mo, const std::string& connector_name);
};
} // namespace GUI