Drain holes are now saved in ModelObject

Internal changes in GLGizmoHollow.cpp
This commit is contained in:
Lukas Matena 2019-11-08 14:05:56 +01:00
parent 9836533cb3
commit 645f13a0ae
6 changed files with 289 additions and 349 deletions

View File

@ -606,6 +606,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
assert(this->config.id() == rhs.config.id());
this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status;
this->sla_drain_holes = rhs.sla_drain_holes;
this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
this->layer_height_profile = rhs.layer_height_profile;
this->printable = rhs.printable;
@ -646,6 +647,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
assert(this->config.id() == rhs.config.id());
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
this->sla_drain_holes = std::move(rhs.sla_drain_holes);
this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation);
@ -1099,6 +1101,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper) {
upper->set_model(nullptr);
upper->sla_support_points.clear();
lower->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes();
upper->input_file = "";
@ -1107,6 +1110,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_lower) {
lower->set_model(nullptr);
lower->sla_support_points.clear();
lower->sla_drain_holes.clear();
lower->sla_points_status = sla::PointsStatus::NoPoints;
lower->clear_volumes();
lower->input_file = "";

View File

@ -203,6 +203,9 @@ public:
// the SLA gizmo and the backend).
sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints;
// Holes to be drilled into the object so resin can flow out
std::vector<sla::DrainHole> sla_drain_holes;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment
@ -372,7 +375,7 @@ private:
template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<ObjectBase>(this));
Internal::StaticSerializationWrapper<ModelConfig> config_wrapper(config);
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, printable, origin_translation,
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
}
};

View File

@ -80,6 +80,39 @@ struct SupportPoint
using SupportPoints = std::vector<SupportPoint>;
struct DrainHole
{
Vec3f m_pos;
Vec3f m_normal;
float m_radius;
float m_height;
DrainHole()
: m_pos(Vec3f::Zero()), m_normal(Vec3f::UnitZ()), m_radius(5.f),
m_height(10.f)
{}
DrainHole(Vec3f position, Vec3f normal, float radius, float height)
: m_pos(position)
, m_normal(normal)
, m_radius(radius)
, m_height(height)
{}
bool operator==(const DrainHole &sp) const
{
return (m_pos == sp.m_pos) && (m_normal == sp.m_normal)
&& is_approx(m_radius, sp.m_radius) && is_approx(m_height, sp.m_height);
}
bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); }
template<class Archive> void serialize(Archive &ar)
{
ar(m_pos, m_normal, m_radius, m_height);
}
};
struct Contour3D;
/// An index-triangle structure for libIGL functions. Also serves as an

View File

@ -52,6 +52,7 @@ bool GLGizmoHollow::on_init()
m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": ";
m_desc["reset_direction"] = _(L("Reset direction"));
m_desc["hollow"] = _(L("Hollow"));
m_desc["show_supports"] = _(L("Show supports"));
return true;
}
@ -243,13 +244,13 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[4];
size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size();
size_t cache_size = m_model_object->sla_drain_holes.size();
for (size_t i = 0; i < cache_size; ++i)
{
const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i];
const bool& point_selected = m_selected[i];
if (is_mesh_point_clipped(support_point.pos.cast<double>()))
if (is_mesh_point_clipped(drain_hole.m_pos.cast<double>()))
continue;
// First decide about the color of the point.
@ -262,7 +263,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
}
else {
render_color[3] = 1.f;
if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active
if (size_t(m_hover_id) == i) {
render_color[0] = 0.f;
render_color[1] = 1.0f;
render_color[2] = 1.0f;
@ -280,43 +281,34 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
glsafe(::glPushMatrix());
glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2)));
glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2)));
glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
if (vol->is_left_handed())
glFrontFace(GL_CW);
// Matrices set, we can render the point mark now.
// If in editing mode, we'll also render a cone pointing to the sphere.
if (m_editing_mode) {
// in case the normal is not yet cached, find and cache it
if (m_editing_cache[i].normal == Vec3f::Zero())
m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
Eigen::AngleAxisd aa(q);
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * drain_hole.m_normal.cast<double>());
Eigen::AngleAxisd aa(q);
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
const double cylinder_radius = double(drain_hole.m_radius) * RenderPointScale; //0.25; // mm
const double stick_out_length = 1.;
const double cone_height = m_new_hole_height + stick_out_length;
glsafe(::glPushMatrix());
glsafe(::glTranslated(0., 0., -cone_height+stick_out_length));
::gluCylinder(m_quadric, cylinder_radius, cylinder_radius, cone_height, 24, 1);
glsafe(::glTranslated(0., 0., cone_height));
::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1);
glsafe(::glTranslated(0., 0., -cone_height));
glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f));
::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1);
glsafe(::glPopMatrix());
const double cone_radius = double(support_point.head_front_radius) * RenderPointScale; //0.25; // mm
const double cone_height = m_new_cone_height;
//const double cone_rad_diff = m_new_cone_angle*(cone_radius/(cone_height/2.))*(cone_height/2.);
const double cone_rad_diff = m_new_cone_angle*cone_radius;
glsafe(::glPushMatrix());
glsafe(::glTranslated(0., 0., -cone_height/2.));
//::gluCylinder(m_quadric, cone_radius, cone_radius, cone_height, 24, 1);
::gluCylinder(m_quadric, cone_radius+cone_rad_diff, cone_radius-cone_rad_diff, cone_height, 24, 1);
glsafe(::glTranslated(0., 0., cone_height));
::gluDisk(m_quadric, 0.0, cone_radius-cone_rad_diff, 24, 1);
glsafe(::glTranslated(0., 0., -cone_height));
glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f));
::gluDisk(m_quadric, 0.0, cone_radius+cone_rad_diff, 24, 1);
glsafe(::glPopMatrix());
}
//::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12);
if (vol->is_left_handed())
glFrontFace(GL_CCW);
glsafe(::glPopMatrix());
}
@ -412,122 +404,121 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
// concludes that the event was not intended for it, it should return false.
bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{
if (m_editing_mode) {
// left down with shift - show the selection rectangle:
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
if (m_hover_id == -1) {
if (shift_down || alt_down) {
m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
}
// left down with shift - show the selection rectangle:
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
if (m_hover_id == -1) {
if (shift_down || alt_down) {
m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
}
}
else {
if (m_selected[m_hover_id])
unselect_point(m_hover_id);
else {
if (m_editing_cache[m_hover_id].selected)
unselect_point(m_hover_id);
else {
if (!alt_down)
select_point(m_hover_id);
}
if (!alt_down)
select_point(m_hover_id);
}
return true;
}
// left down without selection rectangle - place point on the mesh:
if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) {
// If any point is in hover state, this should initiate its move - return control back to GLCanvas:
if (m_hover_id != -1)
return false;
return true;
}
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole")));
m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
m_parent.set_as_dirty();
m_wait_for_up_event = true;
}
else
return false;
// left down without selection rectangle - place point on the mesh:
if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) {
// If any point is in hover state, this should initiate its move - return control back to GLCanvas:
if (m_hover_id != -1)
return false;
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole")));
m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first, pos_and_normal.second, m_new_hole_radius, m_new_hole_height);
m_selected.push_back(false);
assert(m_selected.size == m_model_object->sla_drain_holes.size());
m_parent.set_as_dirty();
m_wait_for_up_event = true;
}
else
select_point(NoPoints);
return false;
}
else
select_point(NoPoints);
return true;
}
// left up with selection rectangle - select points inside the rectangle:
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) {
// Is this a selection or deselection rectangle?
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
// First collect positions of all the points in world coordinates.
Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
std::vector<Vec3d> points;
for (unsigned int i=0; i<m_model_object->sla_drain_holes.size(); ++i)
points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].m_pos.cast<double>());
// Now ask the rectangle which of the points are inside.
std::vector<Vec3f> points_inside;
std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
for (size_t idx : points_idxs)
points_inside.push_back(points[idx].cast<float>());
// Only select/deselect points that are actually visible
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get()))
{
if (rectangle_status == GLSelectionRectangle::Deselect)
unselect_point(points_idxs[idx]);
else
select_point(points_idxs[idx]);
}
return true;
}
// left up with no selection rectangle
if (action == SLAGizmoEventType::LeftUp) {
if (m_wait_for_up_event) {
m_wait_for_up_event = false;
return true;
}
}
// dragging the selection rectangle:
if (action == SLAGizmoEventType::Dragging) {
if (m_wait_for_up_event)
return true; // point has been placed and the button not released yet
// this prevents GLCanvas from starting scene rotation
if (m_selection_rectangle.is_dragging()) {
m_selection_rectangle.dragging(mouse_position);
return true;
}
// left up with selection rectangle - select points inside the rectangle:
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) {
// Is this a selection or deselection rectangle?
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
return false;
}
// First collect positions of all the points in world coordinates.
Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
std::vector<Vec3d> points;
for (unsigned int i=0; i<m_editing_cache.size(); ++i)
points.push_back(trafo.get_matrix() * m_editing_cache[i].support_point.pos.cast<double>());
if (action == SLAGizmoEventType::Delete) {
// delete key pressed
delete_selected_points();
return true;
}
// Now ask the rectangle which of the points are inside.
std::vector<Vec3f> points_inside;
std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
for (size_t idx : points_idxs)
points_inside.push_back(points[idx].cast<float>());
// Only select/deselect points that are actually visible
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get()))
{
if (rectangle_status == GLSelectionRectangle::Deselect)
unselect_point(points_idxs[idx]);
else
select_point(points_idxs[idx]);
}
return true;
}
// left up with no selection rectangle
if (action == SLAGizmoEventType::LeftUp) {
if (m_wait_for_up_event) {
m_wait_for_up_event = false;
return true;
}
}
// dragging the selection rectangle:
if (action == SLAGizmoEventType::Dragging) {
if (m_wait_for_up_event)
return true; // point has been placed and the button not released yet
// this prevents GLCanvas from starting scene rotation
if (m_selection_rectangle.is_dragging()) {
m_selection_rectangle.dragging(mouse_position);
return true;
}
return false;
}
if (action == SLAGizmoEventType::Delete) {
// delete key pressed
if (action == SLAGizmoEventType::RightDown) {
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
delete_selected_points();
return true;
}
return false;
}
if (action == SLAGizmoEventType::RightDown) {
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
delete_selected_points();
return true;
}
return false;
}
if (action == SLAGizmoEventType::SelectAll) {
select_point(AllPoints);
return true;
}
if (action == SLAGizmoEventType::SelectAll) {
select_point(AllPoints);
return true;
}
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
@ -552,39 +543,26 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
void GLGizmoHollow::delete_selected_points(bool force)
{
if (! m_editing_mode) {
std::cout << "DEBUGGING: delete_selected_points called out of editing mode!" << std::endl;
std::abort();
}
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole")));
for (unsigned int idx=0; idx<m_editing_cache.size(); ++idx) {
if (m_editing_cache[idx].selected) {
m_editing_cache.erase(m_editing_cache.begin() + (idx--));
for (unsigned int idx=0; idx<m_model_object->sla_drain_holes.size(); ++idx) {
if (m_selected[idx]) {
m_selected.erase(m_selected.begin()+idx);
m_model_object->sla_drain_holes.erase(m_model_object->sla_drain_holes.begin() + (idx--));
}
}
select_point(NoPoints);
//m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
void GLGizmoHollow::on_update(const UpdateData& data)
{
if (! m_editing_mode)
return;
else {
if (m_hover_id != -1) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (! unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal))
return;
m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first;
m_editing_cache[m_hover_id].support_point.is_new_island = false;
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
// Do not update immediately, wait until the mouse is released.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
if (m_hover_id != -1) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (! unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal))
return;
m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first;
m_model_object->sla_drain_holes[m_hover_id].m_normal = pos_and_normal.second;
}
}
@ -661,17 +639,12 @@ ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const
void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit)
{
if (!m_model_object)
if (! m_model_object)
return;
bool first_run = true; // This is a hack to redraw the button when all points are removed,
// so it is not delayed until the background process finishes.
RENDER_AGAIN:
//m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
//const ImVec2 window_size(m_imgui->scaled(18.f, 16.f));
//ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
//ImGui::SetNextWindowSize(ImVec2(window_size));
const float approx_height = m_imgui->scaled(18.0f);
y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
@ -679,7 +652,6 @@ RENDER_AGAIN:
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f);
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("head_diameter")).x + m_imgui->scaled(1.f);
@ -690,125 +662,72 @@ RENDER_AGAIN:
float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left);
window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx);
bool force_refresh = false;
bool remove_selected = false;
bool remove_all = false;
if (m_editing_mode) {
if (m_imgui->button(m_desc.at("hollow")))
hollow_mesh();
if (m_imgui->button(m_desc.at("hollow")))
hollow_mesh();
float diameter_upper_cap = 20.f; //static_cast<ConfigOptionFloat*>(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value;
if (m_new_hole_radius > diameter_upper_cap)
m_new_hole_radius = diameter_upper_cap;
m_imgui->text(m_desc.at("head_diameter"));
ImGui::SameLine(diameter_slider_left);
ImGui::PushItemWidth(window_width - diameter_slider_left);
float diameter_upper_cap = static_cast<ConfigOptionFloat*>(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value;
if (m_new_point_head_diameter > diameter_upper_cap)
m_new_point_head_diameter = diameter_upper_cap;
m_imgui->text(m_desc.at("head_diameter"));
ImGui::SameLine(diameter_slider_left);
ImGui::PushItemWidth(window_width - diameter_slider_left);
// Following is a nasty way to:
// - save the initial value of the slider before one starts messing with it
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider
float initial_value = m_new_point_head_diameter;
ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f");
if (ImGui::IsItemClicked()) {
if (m_old_point_head_diameter == 0.f)
m_old_point_head_diameter = initial_value;
}
if (ImGui::IsItemEdited()) {
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f;
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
// momentarily restore the old value to take snapshot
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f;
float backup = m_new_point_head_diameter;
m_new_point_head_diameter = m_old_point_head_diameter;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter")));
m_new_point_head_diameter = backup;
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f;
m_old_point_head_diameter = 0.f;
}
// !!!! Something as above should be done for the cone angle
m_imgui->text("Hole taper: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_new_cone_angle, -1.f, 1.f, "%.1f");
m_imgui->text("Hole height: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_new_cone_height, 0.1f, 10.f, "%.1f");
m_imgui->disabled_begin(m_selection_empty);
remove_selected = m_imgui->button(m_desc.at("remove_selected"));
m_imgui->disabled_end();
m_imgui->disabled_begin(m_editing_cache.empty());
remove_all = m_imgui->button(m_desc.at("remove_all"));
m_imgui->disabled_end();
m_imgui->text(" "); // vertical gap
m_imgui->text("Offset: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f");
m_imgui->text("Adaptibility: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f");
// Following is a nasty way to:
// - save the initial value of the slider before one starts messing with it
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider
float initial_value = m_new_hole_radius;
ImGui::SliderFloat("", &m_new_hole_radius, 0.1f, diameter_upper_cap, "%.1f");
if (ImGui::IsItemClicked()) {
if (m_old_hole_radius == 0.f)
m_old_hole_radius = initial_value;
}
else { // not in editing mode:
m_imgui->text(m_desc.at("minimal_distance"));
ImGui::SameLine(settings_sliders_left);
ImGui::PushItemWidth(window_width - settings_sliders_left);
std::vector<const ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider
bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
m_imgui->text(m_desc.at("points_density"));
ImGui::SameLine(settings_sliders_left);
ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%");
slider_clicked |= ImGui::IsItemClicked();
slider_edited |= ImGui::IsItemEdited();
slider_released |= ImGui::IsItemDeactivatedAfterEdit();
if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo
m_minimal_point_distance_stash = minimal_point_distance;
m_density_stash = density;
}
if (slider_edited) {
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
}
if (slider_released) {
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change")));
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
wxGetApp().obj_list()->update_and_show_object_settings_item();
}
m_imgui->disabled_begin(m_normal_cache.empty());
remove_all = m_imgui->button(m_desc.at("remove_all"));
m_imgui->disabled_end();
if (ImGui::IsItemEdited()) {
for (size_t idx=0; idx<m_selected.size(); ++idx)
if (m_selected[idx])
m_model_object->sla_drain_holes[idx].m_radius = m_new_hole_radius;
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
// momentarily restore the old value to take snapshot
for (size_t idx=0; idx<m_selected.size(); ++idx)
if (m_selected[idx])
m_model_object->sla_drain_holes[idx].m_radius = m_old_hole_radius;
float backup = m_new_hole_radius;
m_new_hole_radius = m_old_hole_radius;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter")));
m_new_hole_radius = backup;
for (size_t idx=0; idx<m_selected.size(); ++idx)
if (m_selected[idx])
m_model_object->sla_drain_holes[idx].m_radius = m_new_hole_radius;
m_old_hole_radius = 0.f;
}
// !!!! Something as above should be done for the undo/redo
m_imgui->text("Hole height: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_new_hole_height, 0.1f, 10.f, "%.1f");
m_imgui->disabled_begin(m_selection_empty);
remove_selected = m_imgui->button(m_desc.at("remove_selected"));
m_imgui->disabled_end();
m_imgui->disabled_begin(m_model_object->sla_drain_holes.empty());
remove_all = m_imgui->button(m_desc.at("remove_all"));
m_imgui->disabled_end();
m_imgui->text(" "); // vertical gap
m_imgui->text("Offset: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f");
m_imgui->text("Adaptibility: ");
ImGui::SameLine();
ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f");
// Following is rendered in both editing and non-editing mode:
m_imgui->text("");
@ -827,7 +746,6 @@ RENDER_AGAIN:
if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f"))
update_clipping_plane(true);
if (m_imgui->button("?")) {
wxGetApp().CallAfter([]() {
SlaGizmoHelpDialog help_dlg;
@ -835,18 +753,18 @@ RENDER_AGAIN:
});
}
m_imgui->end();
if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode
m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance);
if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) {
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance);
force_refresh = true;
}
m_old_editing_state = m_editing_mode;
m_imgui->end();
if (remove_selected || remove_all) {
force_refresh = false;
m_parent.set_as_dirty();
bool was_in_editing = m_editing_mode;
if (remove_all) {
select_point(AllPoints);
delete_selected_points(true); // true - delete regardless of locked status
@ -927,12 +845,11 @@ void GLGizmoHollow::on_set_state()
// Set default head diameter from config.
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
m_new_hole_radius = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
}
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
//Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
m_parent.toggle_model_objects_visibility(true);
m_normal_cache.clear();
m_clipping_plane_distance = 0.f;
// Release clippers and the AABB raycaster.
m_object_clipper.reset();
@ -951,27 +868,27 @@ void GLGizmoHollow::on_start_dragging()
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
m_point_before_drag = m_editing_cache[m_hover_id];
m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].m_pos;
}
else
m_point_before_drag = CacheEntry();
m_hole_before_drag = Vec3f::Zero();
}
void GLGizmoHollow::on_stop_dragging()
{
if (m_hover_id != -1) {
CacheEntry backup = m_editing_cache[m_hover_id];
Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].m_pos;
if (m_point_before_drag.support_point.pos != Vec3f::Zero() // some point was touched
&& backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected
if (m_hole_before_drag != Vec3f::Zero() // some point was touched
&& backup != m_hole_before_drag) // and it was moved, not just selected
{
m_editing_cache[m_hover_id] = m_point_before_drag;
m_model_object->sla_drain_holes[m_hover_id].m_pos = m_hole_before_drag;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole")));
m_editing_cache[m_hover_id] = backup;
m_model_object->sla_drain_holes[m_hover_id].m_pos = backup;
}
}
m_point_before_drag = CacheEntry();
m_hole_before_drag = Vec3f::Zero();
}
@ -981,9 +898,8 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar)
ar(m_clipping_plane_distance,
*m_clipping_plane,
m_model_object_id,
m_new_point_head_diameter,
m_normal_cache,
m_editing_cache,
m_new_hole_radius,
m_selected,
m_selection_empty
);
}
@ -995,9 +911,8 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const
ar(m_clipping_plane_distance,
*m_clipping_plane,
m_model_object_id,
m_new_point_head_diameter,
m_normal_cache,
m_editing_cache,
m_new_hole_radius,
m_selected,
m_selection_empty
);
}
@ -1006,63 +921,41 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const
void GLGizmoHollow::select_point(int i)
{
if (! m_editing_mode) {
std::cout << "DEBUGGING: select_point called when out of editing mode!" << std::endl;
std::abort();
}
if (i == AllPoints || i == NoPoints) {
for (auto& point_and_selection : m_editing_cache)
point_and_selection.selected = ( i == AllPoints );
m_selected.assign(m_selected.size(), i == AllPoints);
m_selection_empty = (i == NoPoints);
if (i == AllPoints)
m_new_point_head_diameter = m_editing_cache[0].support_point.head_front_radius * 2.f;
m_new_hole_radius = m_model_object->sla_drain_holes[0].m_radius;
}
else {
m_editing_cache[i].selected = true;
while (size_t(i) >= m_selected.size())
m_selected.push_back(false);
m_selected[i] = true;
m_selection_empty = false;
m_new_point_head_diameter = m_editing_cache[i].support_point.head_front_radius * 2.f;
m_new_hole_radius = m_model_object->sla_drain_holes[i].m_radius;
}
}
void GLGizmoHollow::unselect_point(int i)
{
if (! m_editing_mode) {
std::cout << "DEBUGGING: unselect_point called when out of editing mode!" << std::endl;
std::abort();
}
m_editing_cache[i].selected = false;
m_selected[i] = false;
m_selection_empty = true;
for (const CacheEntry& ce : m_editing_cache) {
if (ce.selected) {
for (const bool sel : m_selected) {
if (sel) {
m_selection_empty = false;
break;
}
}
}
bool GLGizmoHollow::unsaved_changes() const
{
if (m_editing_cache.size() != m_normal_cache.size())
return true;
for (size_t i=0; i<m_editing_cache.size(); ++i)
if (m_editing_cache[i].support_point != m_normal_cache[i])
return true;
return false;
}
void GLGizmoHollow::reload_cache()
{
m_selected.clear();
m_selected.assign(m_model_object->sla_drain_holes.size(), false);
}
void GLGizmoHollow::update_clipping_plane(bool keep_normal) const
{
Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ?

View File

@ -25,7 +25,7 @@ private:
ObjectID m_model_object_id = 0;
int m_active_instance = -1;
float m_active_instance_bb_radius; // to cache the bb
mutable double m_z_shift = 0.f;
mutable double m_z_shift = 0.;
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
const float RenderPointScale = 1.f;
@ -46,27 +46,26 @@ private:
class CacheEntry {
public:
CacheEntry() :
support_point(sla::SupportPoint()), selected(false), normal(Vec3f::Zero()) {}
drain_hole(sla::DrainHole()), selected(false) {}
CacheEntry(const sla::SupportPoint& point, bool sel = false, const Vec3f& norm = Vec3f::Zero()) :
support_point(point), selected(sel), normal(norm) {}
CacheEntry(const sla::DrainHole& point, bool sel = false) :
drain_hole(point), selected(sel) {}
bool operator==(const CacheEntry& rhs) const {
return (support_point == rhs.support_point);
return (drain_hole == rhs.drain_hole);
}
bool operator!=(const CacheEntry& rhs) const {
return ! ((*this) == rhs);
}
sla::SupportPoint support_point;
sla::DrainHole drain_hole;
bool selected; // whether the point is selected
Vec3f normal;
template<class Archive>
void serialize(Archive & ar)
{
ar(support_point, selected, normal);
ar(drain_hole, selected);
}
};
@ -97,17 +96,14 @@ private:
bool unsaved_changes() const;
const TriangleMesh* mesh() const;
bool m_editing_mode = true; // Is editing mode active?
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
float m_new_point_head_diameter; // Size of a new point.
float m_new_cone_angle = 0.f;
float m_new_cone_height = 5.f;
CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited
float m_old_point_head_diameter = 0.; // the same
bool m_show_supports = true;
float m_new_hole_radius; // Size of a new hole.
float m_new_hole_height = 5.f;
float m_old_hole_radius = 0.; // undo/redo - so we know what state was edited
Vec3f m_hole_before_drag = Vec3f::Zero();
float m_minimal_point_distance_stash = 0.f; // and again
float m_density_stash = 0.f; // and again
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected
std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
mutable std::vector<bool> m_selected; // which holes are currently selected
float m_offset = 2.f;
float m_adaptability = 1.f;
@ -148,7 +144,7 @@ protected:
void on_set_hover_id() override
{
if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id)
if (int(m_model_object->sla_drain_holes.size()) <= m_hover_id)
m_hover_id = -1;
}
void on_start_dragging() override;

View File

@ -750,20 +750,31 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
if (evt.GetEventType() == wxEVT_KEY_UP)
{
if (m_current == SlaSupports)
if (m_current == SlaSupports || m_current == Hollow)
{
GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current());
bool is_editing = true;
bool is_rectangle_dragging = false;
if (m_current == SlaSupports) {
GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current());
is_editing = gizmo->is_in_editing_mode();
is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
}
else {
GLGizmoHollow* gizmo = dynamic_cast<GLGizmoHollow*>(get_current());
is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
}
if (keyCode == WXK_SHIFT)
{
// shift has been just released - SLA gizmo might want to close rectangular selection.
if (gizmo_event(SLAGizmoEventType::ShiftUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging()))
if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging))
processed = true;
}
else if (keyCode == WXK_ALT)
{
// alt has been just released - SLA gizmo might want to close rectangular selection.
if (gizmo_event(SLAGizmoEventType::AltUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging()))
if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging))
processed = true;
}
}