CutGizmo: Improvements to identify Upper/Lower parts_count

+ Rework CutDialog
+ Added "Cut to" section (Idea from #9564[Cut: Keep results as parts of current idea] was used, but there is other implementation)
+ Fix for #9657 - Inconvenient gizma, blocks the view of the cut of small parts
+ Add functionality "Flip cut plane" = Fix for #9632 - Adding connectors to either split cut part
+ ImGuiWrapper: added tooltip for button
This commit is contained in:
YuSanka 2023-02-09 08:52:07 +01:00
parent 026ca7b3c9
commit b40473be51
6 changed files with 295 additions and 136 deletions

View File

@ -1477,6 +1477,12 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d&
// Add required cut parts to the objects // Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
add_cut_volume(upper_mesh, lower, volume, cut_matrix);
return;
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix); add_cut_volume(upper_mesh, upper, volume, cut_matrix);
@ -1608,34 +1614,39 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
ModelObjectPtrs res; ModelObjectPtrs res;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !lower->volumes.empty()) {
invalidate_translations(upper, instances[instance]);
reset_instance_transformation(upper, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper),
local_displace);
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
invalidate_translations(lower, instances[instance]);
reset_instance_transformation(lower, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower); res.push_back(lower);
} }
else {
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
invalidate_translations(upper, instances[instance]);
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { reset_instance_transformation(upper, instance, cut_matrix,
for (auto dowel : dowels) { attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
invalidate_translations(dowel, instances[instance]); attributes.has(ModelObjectCutAttribute::FlipUpper),
local_displace);
res.push_back(upper);
}
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
invalidate_translations(lower, instances[instance]);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); reset_instance_transformation(lower, instance, cut_matrix,
dowel->name += "-Dowel-" + dowel->volumes[0]->name; attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
res.push_back(dowel); attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
invalidate_translations(dowel, instances[instance]);
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel);
}
} }
} }

View File

@ -316,7 +316,7 @@ enum class ModelVolumeType : int {
SUPPORT_ENFORCER, SUPPORT_ENFORCER,
}; };
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels }; enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels };
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>; using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);

View File

@ -21,6 +21,8 @@ namespace Slic3r {
namespace GUI { namespace GUI {
static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW();
static const ColorRGBA ABOVE_GRABBER_COLOR = ColorRGBA::CYAN();
static const ColorRGBA BELOW_GRABBER_COLOR = ColorRGBA::MAGENTA();
// connector colors // connector colors
static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW(); static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
@ -196,7 +198,7 @@ static void init_from_angle_arc(GLModel& model, double angle, double radius)
GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id)
, m_connectors_group_id (3) , m_connectors_group_id (GrabberID::Count)
, m_connector_type (CutConnectorType::Plug) , m_connector_type (CutConnectorType::Plug)
, m_connector_style (size_t(CutConnectorStyle::Prizm)) , m_connector_style (size_t(CutConnectorStyle::Prizm))
, m_connector_shape_id (size_t(CutConnectorShape::Circle)) , m_connector_shape_id (size_t(CutConnectorShape::Circle))
@ -227,13 +229,19 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
m_axis_names = { "X", "Y", "Z" }; m_axis_names = { "X", "Y", "Z" };
m_part_orientation_names = {
{"none", _L("Keep orientation")},
{"on_cut", _L("Place on cut")},
{"flip", _L("Flip upside down")},
};
update_connector_shape(); update_connector_shape();
} }
std::string GLGizmoCut3D::get_tooltip() const std::string GLGizmoCut3D::get_tooltip() const
{ {
std::string tooltip; std::string tooltip;
if (m_hover_id == Z) { if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) {
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm"));
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
@ -249,6 +257,11 @@ std::string GLGizmoCut3D::get_tooltip() const
} }
return tooltip; return tooltip;
} }
if (!m_dragging && m_hover_id == CutPlane)
return _u8L("Click to flip the cut plane\n"
"Drag to move the cut plane");
if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) {
std::string axis = m_hover_id == X ? "X" : "Y"; std::string axis = m_hover_id == X ? "X" : "Y";
return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°"); return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°");
@ -278,11 +291,17 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
if (use_grabbers(mouse_event)) { if (use_grabbers(mouse_event)) {
if (m_hover_id >= m_connectors_group_id) { if (m_hover_id >= m_connectors_group_id) {
if (mouse_event.LeftDown() && !mouse_event.CmdDown()&& !mouse_event.AltDown()) if (mouse_event.LeftDown() && !mouse_event.CmdDown() && !mouse_event.AltDown())
unselect_all_connectors(); unselect_all_connectors();
if (mouse_event.LeftUp() && !mouse_event.ShiftDown()) if (mouse_event.LeftUp() && !mouse_event.ShiftDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown());
} }
else if (m_hover_id == CutPlane) {
if (mouse_event.LeftDown())
m_was_cut_plane_dragged = false;
else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged)
flip_cut_plane();
}
return true; return true;
} }
@ -656,12 +675,11 @@ void GLGizmoCut3D::render_cut_plane()
shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("view_model_matrix", view_model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix());
if (can_perform_cut() && has_valid_contour()) if (can_perform_cut() && has_valid_contour())
// m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); m_plane.model.set_color({ 0.8f, 0.8f, 0.8f, 0.5f });
m_plane.set_color({ 0.9f, 0.9f, 0.9f, 0.5f });
else else
m_plane.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); m_plane.model.set_color({ 1.0f, 0.8f, 0.8f, 0.5f });
m_plane.render(); m_plane.model.render();
glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
@ -717,7 +735,7 @@ void GLGizmoCut3D::render_line(GLModel& line_model, const ColorRGBA& color, Tran
} }
} }
void GLGizmoCut3D::render_rotation_snapping(Axis axis, const ColorRGBA& color) void GLGizmoCut3D::render_rotation_snapping(GrabberID axis, const ColorRGBA& color)
{ {
GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat");
if (!line_shader) if (!line_shader)
@ -760,49 +778,34 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
{ {
glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
ColorRGBA color = m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; ColorRGBA color = ColorRGBA::GRAY();
const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m;
const double mean_size = get_grabber_mean_size(bounding_box()); const double mean_size = get_grabber_mean_size(bounding_box());
double size;
double size = m_dragging && m_hover_id == Z ? get_dragging_half_size(mean_size) : get_half_size(mean_size); const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane;
Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); if (!dragging_by_cut_plane) {
Vec3d offset = 1.25 * size * Vec3d::UnitZ(); render_grabber_connection(GRABBER_COLOR, view_matrix);
// render Z grabber // render sphere grabber
if (!m_dragging && m_hover_id < 0)
render_grabber_connection(color, view_matrix);
render_model(m_sphere.model, color, view_matrix * scale_transform(size));
if ((!m_dragging && m_hover_id < 0) || m_hover_id == Z)
{
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
if (tbb.min.z() <= 0.0)
render_model(m_cone.model, color, view_matrix * translation_transform(-offset) * rotation_transform(PI * Vec3d::UnitX()) * scale_transform(cone_scale));
if (tbb.max.z() >= 0.0)
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * scale_transform(cone_scale));
}
// render top sphere for X/Y grabbers
if ((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y)
{
size = m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size); size = m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) :
m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY(); m_hover_id == X ? complementary(ColorRGBA::RED()) :
m_hover_id == Z ? GRABBER_COLOR : ColorRGBA::GRAY();
render_model(m_sphere.model, color, view_matrix * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); render_model(m_sphere.model, color, view_matrix * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size));
} }
const bool no_one_grabber_hovered = !m_dragging && (m_hover_id < 0 || m_hover_id == CutPlane);
// render X grabber // render X grabber
if ((!m_dragging && m_hover_id < 0) || m_hover_id == X) if (no_one_grabber_hovered || m_hover_id == X)
{ {
size = m_dragging && m_hover_id == X ? get_dragging_half_size(mean_size) : get_half_size(mean_size); size = m_dragging && m_hover_id == X ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED();
if (m_hover_id == X) { if (m_hover_id == X) {
@ -810,7 +813,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
render_rotation_snapping(X, color); render_rotation_snapping(X, color);
} }
offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len);
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale));
offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len);
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale));
@ -818,10 +821,10 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
// render Y grabber // render Y grabber
if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y) if (no_one_grabber_hovered || m_hover_id == Y)
{ {
size = m_dragging && m_hover_id == Y ? get_dragging_half_size(mean_size) : get_half_size(mean_size); size = m_dragging && m_hover_id == Y ? get_dragging_half_size(mean_size) : get_half_size(mean_size);
cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN();
if (m_hover_id == Y) { if (m_hover_id == Y) {
@ -829,7 +832,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
render_rotation_snapping(Y, color); render_rotation_snapping(Y, color);
} }
offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); Vec3d offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len);
render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale));
offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len);
render_model(m_cone.model, color, view_matrix * translation_transform(offset)* rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); render_model(m_cone.model, color, view_matrix * translation_transform(offset)* rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale));
@ -940,8 +943,8 @@ void GLGizmoCut3D::on_register_raycasters_for_picking()
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity()));
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity()));
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity()));
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity()));
} }
update_raycasters_for_picking_transform(); update_raycasters_for_picking_transform();
@ -1018,20 +1021,22 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform()
const double size = get_half_size(get_grabber_mean_size(box)); const double size = get_half_size(get_grabber_mean_size(box));
Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
int id = 0;
Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len);
m_raycasters[0]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale));
offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len);
m_raycasters[1]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale));
offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len);
m_raycasters[2]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale));
offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len);
m_raycasters[3]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale));
offset = 1.25 * size * Vec3d::UnitZ(); offset = 1.25 * size * Vec3d::UnitZ();
m_raycasters[4]->set_transform(trafo * scale_transform(size)); m_raycasters[id++]->set_transform(trafo * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size));
m_raycasters[5]->set_transform(trafo * translation_transform(-offset) * rotation_transform(PI * Vec3d::UnitX()) * scale_transform(scale));
m_raycasters[6]->set_transform(trafo * translation_transform(offset) * scale_transform(scale)); m_raycasters[id++]->set_transform(trafo);
} }
} }
@ -1072,7 +1077,7 @@ bool GLGizmoCut3D::on_is_selectable() const
return wxGetApp().get_mode() != comSimple; return wxGetApp().get_mode() != comSimple;
} }
Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& mouse_ray) const
{ {
double half_pi = 0.5 * PI; double half_pi = 0.5 * PI;
@ -1108,13 +1113,11 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse
void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data)
{ {
Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ(); // some Margin const Vec3d grabber_init_pos = (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ();
rotate_vec3d_around_plane_center(starting_box_center); const Vec3d starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * grabber_init_pos;
double projection = 0.0;
const Vec3d&starting_drag_position = m_plane_center; Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ();
double projection = 0.0;
Vec3d starting_vec = starting_drag_position - starting_box_center;
if (starting_vec.norm() != 0.0) { if (starting_vec.norm() != 0.0) {
Vec3d mouse_dir = data.mouse_ray.unit_vector(); Vec3d mouse_dir = data.mouse_ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
@ -1130,17 +1133,19 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data)
projection = inters_vec.dot(starting_vec); projection = inters_vec.dot(starting_vec);
} }
if (wxGetKeyState(WXK_SHIFT)) if (wxGetKeyState(WXK_SHIFT))
projection = m_snap_step * (double)std::round(projection / m_snap_step); projection = m_snap_step * std::round(projection / m_snap_step);
const Vec3d shift = starting_vec * projection; const Vec3d shift = starting_vec * projection;
// move cut plane center // move cut plane center
set_center(m_plane_center + shift); set_center(m_plane_center + shift);
m_was_cut_plane_dragged = true;
} }
void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
{ {
const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray)); const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((GrabberID)m_hover_id, data.mouse_ray));
const Vec2d orig_dir = Vec2d::UnitX(); const Vec2d orig_dir = Vec2d::UnitX();
const Vec2d new_dir = mouse_pos.normalized(); const Vec2d new_dir = mouse_pos.normalized();
@ -1197,7 +1202,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data)
{ {
if (m_hover_id < 0) if (m_hover_id < 0)
return; return;
if (m_hover_id == Z) if (m_hover_id == Z || m_hover_id == CutPlane)
dragging_grabber_z(data); dragging_grabber_z(data);
else if (m_hover_id == X || m_hover_id == Y) else if (m_hover_id == X || m_hover_id == Y)
dragging_grabber_xy(data); dragging_grabber_xy(data);
@ -1223,8 +1228,9 @@ void GLGizmoCut3D::on_stop_dragging()
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction);
m_start_dragging_m = m_rotation_m; m_start_dragging_m = m_rotation_m;
} }
else if (m_hover_id == Z) { else if (m_hover_id == Z || m_hover_id == CutPlane) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); if (m_was_cut_plane_dragged)
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction);
m_ar_plane_center = m_plane_center; m_ar_plane_center = m_plane_center;
} }
} }
@ -1332,7 +1338,7 @@ bool GLGizmoCut3D::update_bb()
set_center_pos(m_bb_center, true); set_center_pos(m_bb_center, true);
m_radius = box.radius(); m_radius = box.radius();
m_grabber_connection_len = 0.75 * m_radius;// std::min<double>(0.75 * m_radius, 35.0); m_grabber_connection_len = 0.5 * m_radius;// std::min<double>(0.75 * m_radius, 35.0);
m_grabber_radius = m_grabber_connection_len * 0.85; m_grabber_radius = m_grabber_connection_len * 0.85;
m_snap_coarse_in_radius = m_grabber_radius / 3.0; m_snap_coarse_in_radius = m_grabber_radius / 3.0;
@ -1372,6 +1378,14 @@ void GLGizmoCut3D::init_picking_models()
m_sphere.model.init_from(its); m_sphere.model.init_from(its);
m_sphere.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its))); m_sphere.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
} }
if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
const double cp_width = 0.02 * get_grabber_mean_size(bounding_box());
indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4);
m_plane.model.init_from(its);
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
}
if (m_shapes.empty()) if (m_shapes.empty())
init_connector_shapes(); init_connector_shapes();
} }
@ -1392,18 +1406,6 @@ void GLGizmoCut3D::init_rendering_items()
} }
if (!m_angle_arc.is_initialized() || m_angle != 0.0) if (!m_angle_arc.is_initialized() || m_angle != 0.0)
init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len);
if (!m_plane.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
#if 1
const double cp_width = 0.02 * get_grabber_mean_size(bounding_box());
m_plane.init_from(its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4));
#else
if (m_cut_plane_as_circle)
m_plane.init_from(its_make_frustum_dowel(2. * m_radius, 0.3, 180));
else
m_plane.init_from(its_make_square_plane(float(m_radius)));
#endif
}
} }
void GLGizmoCut3D::render_clipper_cut() void GLGizmoCut3D::render_clipper_cut()
@ -1566,6 +1568,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
reset_connectors(); reset_connectors();
m_imgui->disabled_end(); m_imgui->disabled_end();
render_flip_plane_button(m_connectors_editing && connectors.empty());
m_imgui->text(_L("Type")); m_imgui->text(_L("Type"));
bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug);
type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel);
@ -1660,6 +1664,58 @@ void GLGizmoCut3D::set_connectors_editing(bool connectors_editing)
m_parent.request_extra_frame(); m_parent.request_extra_frame();
} }
void GLGizmoCut3D::flip_cut_plane()
{
m_rotation_m = m_start_dragging_m * rotation_transform(PI * Vec3d::UnitX());
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Flip cut plane"), UndoRedo::SnapshotType::GizmoAction);
m_start_dragging_m = m_rotation_m;
update_clipper();
}
void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/)
{
ImGui::SameLine(2.5 * m_label_width);
if (m_hover_id == CutPlane)
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered));
m_imgui->disabled_begin(disable_pred);
if (m_imgui->button(_L("Flip cut plane")))
flip_cut_plane();
m_imgui->disabled_end();
if (m_hover_id == CutPlane)
ImGui::PopStyleColor();
}
void GLGizmoCut3D::add_vertical_scaled_interval(float interval)
{
ImGui::GetCurrentWindow()->DC.CursorPos.y += m_imgui->scaled(interval);
}
void GLGizmoCut3D::add_horizontal_scaled_interval(float interval)
{
ImGui::GetCurrentWindow()->DC.CursorPos.x += m_imgui->scaled(interval);
}
void GLGizmoCut3D::add_horizontal_shift(float shift)
{
ImGui::GetCurrentWindow()->DC.CursorPos.x += shift;
}
void GLGizmoCut3D::render_color_marker(float size, const ImU32& color)
{
ImGui::SameLine();
const float radius = 0.5f * size;
ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
pos.x += size;
pos.y += 1.25f * radius;
ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, color, 6);
ImGuiWrapper::text(" ");
}
void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
{ {
// WIP : cut plane mode // WIP : cut plane mode
@ -1667,65 +1723,130 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
if (m_mode == size_t(CutMode::cutPlanar)) { if (m_mode == size_t(CutMode::cutPlanar)) {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(wxString(ImGui::InfoMarkerSmall)); ImGuiWrapper::text(wxString(ImGui::InfoMarkerSmall));
ImGui::SameLine(); ImGui::SameLine();
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT,
get_wraped_wxString(_L("Hold SHIFT key and connect some two points of an object to cut by line"), 40)); get_wraped_wxString(_L("Hold SHIFT key to draw a cut line"), 40));
ImGui::Separator(); ImGui::Separator();
render_build_size(); render_build_size();
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Cut position: ")); ImGuiWrapper::text(_L("Cut position: "));
ImGui::SameLine(m_label_width); ImGui::SameLine(m_label_width);
render_move_center_input(Z); render_move_center_input(Z);
ImGui::SameLine(); ImGui::SameLine();
const bool has_connectors = !connectors.empty();
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center; const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center;
m_imgui->disabled_begin(is_cut_plane_init); m_imgui->disabled_begin(is_cut_plane_init);
if (render_reset_button("cut_plane", _u8L("Reset cutting plane"))) if (render_reset_button("cut_plane", _u8L("Reset cutting plane")))
reset_cut_plane(); reset_cut_plane();
m_imgui->disabled_end(); m_imgui->disabled_end();
// render_flip_plane_button();
add_vertical_scaled_interval(0.75f);
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower);
if (m_imgui->button(_L("Add/Edit connectors"))) if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors")))
set_connectors_editing(true); set_connectors_editing(true);
m_imgui->disabled_end(); m_imgui->disabled_end();
ImGui::SameLine(2.5f * m_label_width);
m_imgui->disabled_begin(is_cut_plane_init && !has_connectors);
if (m_imgui->button(_L("Reset cut"), _L("Reset cutting plane and remove connectors"))) {
reset_cut_plane();
reset_connectors();
}
m_imgui->disabled_end();
ImGui::Separator(); ImGui::Separator();
float label_width = 0; // render "After Cut" section
for (const wxString& label : {_L("Upper part"), _L("Lower part")}) {
const float width = m_imgui->calc_text_size(label).x + m_imgui->scaled(1.5f); ImVec2 label_size;
if (label_width < width) for (const auto& item : m_part_orientation_names) {
label_width = width; const ImVec2 text_size = ImGuiWrapper::calc_text_size(item.second);
if (label_size.x < text_size.x)
label_size.x = text_size.x;
if (label_size.y < text_size.y)
label_size.y = text_size.y;
} }
const float h_shift = label_size.x + m_imgui->scaled(3.f);
auto render_part_action_line = [this, label_width, connectors](const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { const float marker_size = label_size.y;
auto render_part_name = [this, marker_size, has_connectors](const wxString& name, bool& keep_part, const ImU32& color) {
bool keep = true; bool keep = true;
ImGui::AlignTextToFramePadding(); add_horizontal_shift(m_imgui->scaled(1.2f));
m_imgui->text(label); m_imgui->checkbox((m_keep_as_parts ? _L("Part") : _L("Object")) + " " + name, has_connectors ? keep : keep_part);
render_color_marker(marker_size, color);
};
ImGui::SameLine(label_width); auto render_part_actions = [this, h_shift] (const wxString& suffix, const bool& keep_part, bool& place_on_cut_part, bool& rotate_part)
{
m_imgui->disabled_begin(!connectors.empty()); float shift = m_imgui->scaled(1.2f);
m_imgui->checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); if (suffix == "##lower")
m_imgui->disabled_end(); shift += h_shift;
m_imgui->disabled_begin(!keep_part || m_keep_as_parts);
ImGui::SameLine(); add_horizontal_shift(shift);
if (m_imgui->radio_button(m_part_orientation_names.at("none") + suffix, !place_on_cut_part && !rotate_part)) {
m_imgui->disabled_begin(!keep_part);
if (m_imgui->checkbox(_L("Place on cut") + suffix, place_on_cut_part))
rotate_part = false; rotate_part = false;
ImGui::SameLine();
if (m_imgui->checkbox(_L("Flip") + suffix, rotate_part))
place_on_cut_part = false; place_on_cut_part = false;
}
add_horizontal_shift(shift);
if (m_imgui->radio_button(m_part_orientation_names.at("on_cut") + suffix, place_on_cut_part)) {
place_on_cut_part = !place_on_cut_part;
rotate_part = false;
}
add_horizontal_shift(shift);
if (m_imgui->radio_button(m_part_orientation_names.at("flip") + suffix, rotate_part)) {
rotate_part = !rotate_part;
place_on_cut_part = false;
}
m_imgui->disabled_end(); m_imgui->disabled_end();
}; };
m_imgui->text(_L("After cut") + ": "); ImGuiWrapper::text(_L("After cut") + ": ");
render_part_action_line( _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); add_vertical_scaled_interval(0.5f);
render_part_action_line( _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower);
m_imgui->disabled_begin(has_connectors || m_keep_as_parts);
render_part_name("A", m_keep_upper, m_imgui->to_ImU32(ABOVE_GRABBER_COLOR));
ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x);
render_part_name("B", m_keep_lower, m_imgui->to_ImU32(BELOW_GRABBER_COLOR));
m_imgui->disabled_end();
add_vertical_scaled_interval(0.5f);
const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
render_part_actions("##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper);
ImGui::GetCurrentWindow()->DC.CursorPos = pos;
render_part_actions("##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower);
add_vertical_scaled_interval(0.75f);
m_imgui->disabled_begin(has_connectors);
add_horizontal_shift(m_imgui->scaled(/*1*/.2f));
ImGuiWrapper::text(_L("Cut to") + ":");
ImGui::SameLine();
if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts))
m_keep_as_parts = false;
ImGui::SameLine();
if (m_imgui->radio_button(_L("Parts"), m_keep_as_parts))
m_keep_as_parts = true;
if (m_keep_as_parts) {
m_keep_upper = m_keep_lower = true;
m_place_on_cut_upper = m_place_on_cut_lower = false;
m_rotate_upper = m_rotate_lower = false;
}
m_imgui->disabled_end();
} }
ImGui::Separator(); ImGui::Separator();
@ -2110,6 +2231,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
plater->cut(object_idx, instance_idx, translation_transform(cut_center_offset) * m_rotation_m, plater->cut(object_idx, instance_idx, translation_transform(cut_center_offset) * m_rotation_m,
only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) |
only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) |
only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) |
only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) |

View File

@ -6,6 +6,7 @@
#include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GLModel.hpp"
#include "libslic3r/TriangleMesh.hpp" #include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "imgui/imgui.h"
namespace Slic3r { namespace Slic3r {
@ -20,6 +21,14 @@ enum class SLAGizmoEventType : unsigned char;
class GLGizmoCut3D : public GLGizmoBase class GLGizmoCut3D : public GLGizmoBase
{ {
enum GrabberID {
X = 0,
Y,
Z,
CutPlane,
Count,
};
Transform3d m_rotation_m{ Transform3d::Identity() }; Transform3d m_rotation_m{ Transform3d::Identity() };
double m_snap_step{ 1.0 }; double m_snap_step{ 1.0 };
int m_connectors_group_id; int m_connectors_group_id;
@ -57,10 +66,10 @@ class GLGizmoCut3D : public GLGizmoBase
Vec2d m_ldown_mouse_position{ Vec2d::Zero() }; Vec2d m_ldown_mouse_position{ Vec2d::Zero() };
GLModel m_plane;
GLModel m_grabber_connection; GLModel m_grabber_connection;
GLModel m_cut_line; GLModel m_cut_line;
PickingModel m_plane;
PickingModel m_sphere; PickingModel m_sphere;
PickingModel m_cone; PickingModel m_cone;
std::map<CutConnectorAttributes, PickingModel> m_shapes; std::map<CutConnectorAttributes, PickingModel> m_shapes;
@ -90,6 +99,7 @@ class GLGizmoCut3D : public GLGizmoBase
bool m_keep_upper{ true }; bool m_keep_upper{ true };
bool m_keep_lower{ true }; bool m_keep_lower{ true };
bool m_keep_as_parts{ false };
bool m_place_on_cut_upper{ true }; bool m_place_on_cut_upper{ true };
bool m_place_on_cut_lower{ false }; bool m_place_on_cut_lower{ false };
bool m_rotate_upper{ false }; bool m_rotate_upper{ false };
@ -121,6 +131,7 @@ class GLGizmoCut3D : public GLGizmoBase
GLSelectionRectangle m_selection_rectangle; GLSelectionRectangle m_selection_rectangle;
bool m_has_invalid_connector{ false }; bool m_has_invalid_connector{ false };
bool m_was_cut_plane_dragged { false };
bool m_show_shortcuts{ false }; bool m_show_shortcuts{ false };
std::vector<std::pair<wxString, wxString>> m_shortcuts; std::vector<std::pair<wxString, wxString>> m_shortcuts;
@ -154,6 +165,8 @@ class GLGizmoCut3D : public GLGizmoBase
std::vector<std::string> m_axis_names; std::vector<std::string> m_axis_names;
std::map<std::string, wxString> m_part_orientation_names;
public: public:
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@ -192,7 +205,7 @@ protected:
void on_set_hover_id() override; void on_set_hover_id() override;
bool on_is_activable() const override; bool on_is_activable() const override;
bool on_is_selectable() const override; bool on_is_selectable() const override;
Vec3d mouse_position_in_local_plane(Axis axis, const Linef3&mouse_ray) const; Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const;
void dragging_grabber_z(const GLGizmoBase::UpdateData &data); void dragging_grabber_z(const GLGizmoBase::UpdateData &data);
void dragging_grabber_xy(const GLGizmoBase::UpdateData &data); void dragging_grabber_xy(const GLGizmoBase::UpdateData &data);
void dragging_connector(const GLGizmoBase::UpdateData &data); void dragging_connector(const GLGizmoBase::UpdateData &data);
@ -211,6 +224,12 @@ protected:
void render_build_size(); void render_build_size();
void reset_cut_plane(); void reset_cut_plane();
void set_connectors_editing(bool connectors_editing); void set_connectors_editing(bool connectors_editing);
void flip_cut_plane();
void render_flip_plane_button(bool disable_pred = false);
void add_vertical_scaled_interval(float interval);
void add_horizontal_scaled_interval(float interval);
void add_horizontal_shift(float shift);
void render_color_marker(float size, const ImU32& color);
void render_cut_plane_input_window(CutConnectors &connectors); void render_cut_plane_input_window(CutConnectors &connectors);
void init_input_window_data(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors);
void render_input_window_warning() const; void render_input_window_warning() const;
@ -258,7 +277,7 @@ private:
void render_cut_plane(); void render_cut_plane();
void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
void render_rotation_snapping(Axis axis, const ColorRGBA& color); void render_rotation_snapping(GrabberID axis, const ColorRGBA& color);
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix);
void render_cut_plane_grabbers(); void render_cut_plane_grabbers();
void render_cut_line(); void render_cut_line();

View File

@ -453,10 +453,17 @@ void ImGuiWrapper::end()
ImGui::End(); ImGui::End();
} }
bool ImGuiWrapper::button(const wxString &label) bool ImGuiWrapper::button(const wxString &label, const wxString& tooltip)
{ {
auto label_utf8 = into_u8(label); auto label_utf8 = into_u8(label);
return ImGui::Button(label_utf8.c_str()); const bool ret = ImGui::Button(label_utf8.c_str());
if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) {
auto tooltip_utf8 = into_u8(tooltip);
ImGui::SetTooltip(tooltip_utf8.c_str());
}
return ret;
} }
bool ImGuiWrapper::button(const wxString& label, float width, float height) bool ImGuiWrapper::button(const wxString& label, float width, float height)

View File

@ -91,7 +91,7 @@ public:
bool begin(const wxString& name, bool* close, int flags = 0); bool begin(const wxString& name, bool* close, int flags = 0);
void end(); void end();
bool button(const wxString &label); bool button(const wxString &label, const wxString& tooltip = {});
bool button(const wxString& label, float width, float height); bool button(const wxString& label, float width, float height);
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f) bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
bool radio_button(const wxString &label, bool active); bool radio_button(const wxString &label, bool active);