Add section view feature for 3D canvas (#15879)

This commit is contained in:
Ian Bassi
2026-10-02 11:50:43 -03:00
committed by GitHub
parent 6c6e8be43d
commit a1ad2b4425
31 changed files with 599 additions and 612 deletions
+4
View File
@@ -76,6 +76,10 @@ public:
// caller decides which of the two it varies with the realistic view setting.
//
void set_tone(float exposure, float saturation);
//
// ORCA: section view. Cuts away where dot(plane, (x, y, z, 1)) < 0; { 0, 0, 0, 1 } keeps all.
//
void set_clipping_plane(const std::array<float, 4>& plane);
//
// ************************************************************************
+61 -4
View File
@@ -38,6 +38,8 @@ static const char* Segments_Vertex_Shader =
// rejection discards most of the hidden fragments; set when the camera looks down on the print
"uniform int reverse_order;\n"
"uniform int instance_count;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in int vertex_id;\n"
"out vec3 color;\n"
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
@@ -48,6 +50,9 @@ static const char* Segments_Vertex_Shader =
"// needs its world position and, for the depth bias, its eye space normal.\n"
"out vec3 world_position;\n"
"out vec3 shadow_normal;\n"
// ORCA: section view - the segment and the light on its cut face
"flat out int segment_id;\n"
"out vec3 cut_color_direct;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -159,6 +164,9 @@ static const char* Segments_Vertex_Shader =
" color_direct = color_base * direct_lighting(eye_position, eye_normal);\n"
" world_position = pos;\n"
" shadow_normal = eye_normal;\n"
" segment_id = id_a;\n"
" cut_color_direct = clipping_plane.xyz == vec3(0.0) ? vec3(0.0) :\n"
" color_base * direct_lighting(eye_position, mat3(view_matrix) * -clipping_plane.xyz);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
@@ -178,23 +186,33 @@ static const char* Segments_Fragment_Shader =
// passes in each mode is decided in GLCanvas3D::_render_gcode, not here.
"uniform float exposure;\n"
"uniform float saturation;\n"
// ORCA: section view - only camera-facing faces are drawn, so cut faces are ray cast
"uniform vec4 clipping_plane;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform vec3 camera_position;\n"
"uniform samplerBuffer position_tex;\n"
"uniform samplerBuffer height_width_angle_tex;\n"
"const vec3 UP = vec3(0, 0, 1);\n"
"const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n"
"in vec3 color;\n"
"in vec3 color_direct;\n"
"in vec3 world_position;\n"
"in vec3 shadow_normal;\n"
"flat in int segment_id;\n"
"in vec3 cut_color_direct;\n"
"out vec4 fragment_color;\n"
"float shadow_shade() {\n"
"float shadow_shade(vec3 position, vec3 normal) {\n"
" if (shadow_intensity <= 0.0)\n"
" return 1.0;\n"
" vec4 lp = shadow_light_vp * vec4(world_position, 1.0);\n"
" vec4 lp = shadow_light_vp * vec4(position, 1.0);\n"
" vec3 proj = lp.xyz / lp.w;\n"
" proj = proj * 0.5 + 0.5;\n"
" if (proj.z > 1.0)\n"
" return 1.0;\n"
" // Slope-scaled bias, as in gouraud.fs. An extrusion is only a handful of shadow-map texels\n"
" // wide, so grazing faces need the larger bias to keep self-shadow acne off the top surfaces.\n"
" float NdotL = dot(normalize(shadow_normal), SHADOW_LIGHT_DIR);\n"
" float NdotL = dot(normalize(normal), SHADOW_LIGHT_DIR);\n"
" float bias = mix(0.0004, 0.004, clamp(1.0 - NdotL, 0.0, 1.0));\n"
" float sum = 0.0;\n"
" for (int x = -2; x <= 2; ++x) {\n"
@@ -205,8 +223,39 @@ static const char* Segments_Fragment_Shader =
" }\n"
" return 1.0 - shadow_intensity * (sum / 25.0);\n"
"}\n"
// ORCA: section view - where the view ray meets the plane, if inside the extrusion's diamond section
"bool find_cut(out vec3 cut) {\n"
// parallel rays for an orthographic projection
" vec3 ray = projection_matrix[3][3] == 1.0 ? -vec3(view_matrix[0][2], view_matrix[1][2], view_matrix[2][2]) :\n"
" normalize(world_position - camera_position);\n"
" float approach = dot(clipping_plane.xyz, ray);\n"
" if (approach <= 0.0)\n"
" return false;\n"
" cut = world_position - dot(vec4(world_position, 1.0), clipping_plane) / approach * ray;\n"
" vec3 pos_a = texelFetch(position_tex, segment_id).xyz;\n"
" vec3 line = texelFetch(position_tex, segment_id + 1).xyz - pos_a;\n"
" float length_sq = dot(line, line);\n"
" float along = length_sq < 1e-8 ? -1.0 : dot(cut - pos_a, line) / length_sq;\n"
" if (along < 0.0 || along > 1.0)\n"
" return false;\n"
" vec3 line_dir = line * inversesqrt(length_sq);\n"
" vec3 right_dir = abs(dot(line_dir, UP)) > 0.9 ? normalize(cross(vec3(1, 0, 0), line_dir)) : normalize(cross(line_dir, UP));\n"
" vec3 offset = cut - pos_a - along * line;\n"
" vec2 half_height_width = 0.5 * mix(texelFetch(height_width_angle_tex, segment_id).xy,\n"
" texelFetch(height_width_angle_tex, segment_id + 1).xy, along);\n"
" return abs(dot(offset, right_dir)) / half_height_width.y + abs(dot(offset, cross(right_dir, line_dir))) / half_height_width.x <= 1.0;\n"
"}\n"
"void main() {\n"
" vec3 c = (color + color_direct * shadow_shade()) * exposure;\n"
" vec3 c;\n"
" if (dot(vec4(world_position, 1.0), clipping_plane) >= 0.0)\n"
" c = color + color_direct * shadow_shade(world_position, shadow_normal);\n"
" else {\n"
" vec3 cut;\n"
" if (!find_cut(cut))\n"
" discard;\n"
" c = color + cut_color_direct * shadow_shade(cut, mat3(view_matrix) * -clipping_plane.xyz);\n"
" }\n"
" c *= exposure;\n"
" c = mix(vec3(dot(c, LUMA)), c, saturation);\n"
" fragment_color = vec4(clamp(c, 0.0, 1.0), 1.0);\n"
"}\n";
@@ -234,9 +283,12 @@ static const char* Options_Vertex_Shader =
"uniform samplerBuffer height_width_angle_tex;\n"
"uniform samplerBuffer color_tex;\n"
"uniform usamplerBuffer segment_index_tex;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out vec3 color;\n"
"out float clipping_plane_dot;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -263,6 +315,8 @@ static const char* Options_Vertex_Shader =
" height_width.y, 0.0, 0.0,\n"
" 0.0, height_width.y, 0.0,\n"
" 0.0, 0.0, height_width.x);\n"
// ORCA: section view - markers are cut away whole, by their centre
" clipping_plane_dot = dot(vec4(offset, 1.0), clipping_plane);\n"
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
" // ORCA: Apply bias to z-position to avoid z-fighting\n"
" eye_position.z += bias;\n"
@@ -275,8 +329,11 @@ static const char* Options_Vertex_Shader =
static const char* Options_Fragment_Shader =
"#version 150\n"
"in vec3 color;\n"
"in float clipping_plane_dot;\n"
"out vec4 fragment_color;\n"
"void main() {\n"
" if (clipping_plane_dot < 0.0)\n"
" discard;\n"
" fragment_color = vec4(color, 1.0);\n"
"}\n";
+67 -4
View File
@@ -32,6 +32,8 @@ static const char* Segments_Vertex_Shader_ES =
"uniform sampler2D height_width_angle_tex;\n"
"uniform sampler2D color_tex;\n"
"uniform usampler2D segment_index_tex;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in float vertex_id_float;\n"
"out vec3 color;\n"
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
@@ -42,6 +44,9 @@ static const char* Segments_Vertex_Shader_ES =
"// needs its world position and, for the depth bias, its eye space normal.\n"
"out vec3 world_position;\n"
"out vec3 shadow_normal;\n"
// ORCA: section view - the segment and the light on its cut face
"flat out int segment_id;\n"
"out vec3 cut_color_direct;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -156,12 +161,17 @@ static const char* Segments_Vertex_Shader_ES =
" color_direct = color_base * direct_lighting(eye_position, eye_normal);\n"
" world_position = pos;\n"
" shadow_normal = eye_normal;\n"
" segment_id = id_a;\n"
" cut_color_direct = clipping_plane.xyz == vec3(0.0) ? vec3(0.0) :\n"
" color_base * direct_lighting(eye_position, mat3(view_matrix) * -clipping_plane.xyz);\n"
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
"}\n";
static const char* Segments_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
// ORCA: section view - segment ids exceed mediump
"precision highp int;\n"
"// ORCA: sampler2D defaults to lowp in an ES fragment shader, far too coarse to compare\n"
"// shadow map depths against.\n"
"precision highp sampler2D;\n"
@@ -179,23 +189,33 @@ static const char* Segments_Fragment_Shader_ES =
// passes in each mode is decided in GLCanvas3D::_render_gcode, not here.
"uniform float exposure;\n"
"uniform float saturation;\n"
// ORCA: section view - cut faces are ray cast; data samplers stay lowp as in the vertex shader
"uniform vec4 clipping_plane;\n"
"uniform mat4 view_matrix;\n"
"uniform mat4 projection_matrix;\n"
"uniform vec3 camera_position;\n"
"uniform lowp sampler2D position_tex;\n"
"uniform lowp sampler2D height_width_angle_tex;\n"
"const vec3 UP = vec3(0, 0, 1);\n"
"const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n"
"in vec3 color;\n"
"in vec3 color_direct;\n"
"in vec3 world_position;\n"
"in vec3 shadow_normal;\n"
"flat in int segment_id;\n"
"in vec3 cut_color_direct;\n"
"out vec4 fragment_color;\n"
"float shadow_shade() {\n"
"float shadow_shade(vec3 position, vec3 normal) {\n"
" if (shadow_intensity <= 0.0)\n"
" return 1.0;\n"
" vec4 lp = shadow_light_vp * vec4(world_position, 1.0);\n"
" vec4 lp = shadow_light_vp * vec4(position, 1.0);\n"
" vec3 proj = lp.xyz / lp.w;\n"
" proj = proj * 0.5 + 0.5;\n"
" if (proj.z > 1.0)\n"
" return 1.0;\n"
" // Slope-scaled bias, as in gouraud.fs. An extrusion is only a handful of shadow-map texels\n"
" // wide, so grazing faces need the larger bias to keep self-shadow acne off the top surfaces.\n"
" float NdotL = dot(normalize(shadow_normal), SHADOW_LIGHT_DIR);\n"
" float NdotL = dot(normalize(normal), SHADOW_LIGHT_DIR);\n"
" float bias = mix(0.0004, 0.004, clamp(1.0 - NdotL, 0.0, 1.0));\n"
" float sum = 0.0;\n"
" for (int x = -2; x <= 2; ++x) {\n"
@@ -206,8 +226,43 @@ static const char* Segments_Fragment_Shader_ES =
" }\n"
" return 1.0 - shadow_intensity * (sum / 25.0);\n"
"}\n"
"vec4 fetch(lowp sampler2D sampler, int id) {\n"
" ivec2 tex_size = textureSize(sampler, 0);\n"
" return texelFetch(sampler, (tex_size.y == 1) ? ivec2(id, 0) : ivec2(id % tex_size.x, id / tex_size.x), 0);\n"
"}\n"
// ORCA: section view - where the view ray meets the plane, if inside the extrusion's diamond section
"bool find_cut(out vec3 cut) {\n"
// parallel rays for an orthographic projection
" vec3 ray = projection_matrix[3][3] == 1.0 ? -vec3(view_matrix[0][2], view_matrix[1][2], view_matrix[2][2]) :\n"
" normalize(world_position - camera_position);\n"
" float approach = dot(clipping_plane.xyz, ray);\n"
" if (approach <= 0.0)\n"
" return false;\n"
" cut = world_position - dot(vec4(world_position, 1.0), clipping_plane) / approach * ray;\n"
" vec3 pos_a = fetch(position_tex, segment_id).xyz;\n"
" vec3 line = fetch(position_tex, segment_id + 1).xyz - pos_a;\n"
" float length_sq = dot(line, line);\n"
" float along = length_sq < 1e-8 ? -1.0 : dot(cut - pos_a, line) / length_sq;\n"
" if (along < 0.0 || along > 1.0)\n"
" return false;\n"
" vec3 line_dir = line * inversesqrt(length_sq);\n"
" vec3 right_dir = abs(dot(line_dir, UP)) > 0.9 ? normalize(cross(vec3(1, 0, 0), line_dir)) : normalize(cross(line_dir, UP));\n"
" vec3 offset = cut - pos_a - along * line;\n"
" vec2 half_height_width = 0.5 * mix(fetch(height_width_angle_tex, segment_id).xy,\n"
" fetch(height_width_angle_tex, segment_id + 1).xy, along);\n"
" return abs(dot(offset, right_dir)) / half_height_width.y + abs(dot(offset, cross(right_dir, line_dir))) / half_height_width.x <= 1.0;\n"
"}\n"
"void main() {\n"
" vec3 c = (color + color_direct * shadow_shade()) * exposure;\n"
" vec3 c;\n"
" if (dot(vec4(world_position, 1.0), clipping_plane) >= 0.0)\n"
" c = color + color_direct * shadow_shade(world_position, shadow_normal);\n"
" else {\n"
" vec3 cut;\n"
" if (!find_cut(cut))\n"
" discard;\n"
" c = color + cut_color_direct * shadow_shade(cut, mat3(view_matrix) * -clipping_plane.xyz);\n"
" }\n"
" c *= exposure;\n"
" c = mix(vec3(dot(c, LUMA)), c, saturation);\n"
" fragment_color = vec4(clamp(c, 0.0, 1.0), 1.0);\n"
"}\n";
@@ -230,9 +285,12 @@ static const char* Options_Vertex_Shader_ES =
"uniform sampler2D height_width_angle_tex;\n"
"uniform sampler2D color_tex;\n"
"uniform usampler2D segment_index_tex;\n"
// ORCA: section view
"uniform vec4 clipping_plane;\n"
"in vec3 in_position;\n"
"in vec3 in_normal;\n"
"out vec3 color;\n"
"out float clipping_plane_dot;\n"
"vec3 decode_color(float color) {\n"
" int c = int(round(color));\n"
" int r = (c >> 16) & 0xFF;\n"
@@ -264,6 +322,8 @@ static const char* Options_Vertex_Shader_ES =
" height_width.y, 0.0, 0.0,\n"
" 0.0, height_width.y, 0.0,\n"
" 0.0, 0.0, height_width.x);\n"
// ORCA: section view - markers are cut away whole, by their centre
" clipping_plane_dot = dot(vec4(offset, 1.0), clipping_plane);\n"
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
" vec3 eye_normal = (view_matrix * vec4(in_normal, 0.0)).xyz;\n"
" vec3 color_base = decode_color(texelFetch(color_tex, tex_coord(color_tex, id), 0).r);\n"
@@ -275,8 +335,11 @@ static const char* Options_Fragment_Shader_ES =
"#version 300 es\n"
"precision highp float;\n"
"in vec3 color;\n"
"in float clipping_plane_dot;\n"
"out vec4 fragment_color;\n"
"void main() {\n"
" if (clipping_plane_dot < 0.0)\n"
" discard;\n"
" fragment_color = vec4(color, 1.0);\n"
"}\n";
+5
View File
@@ -57,6 +57,11 @@ void Viewer::set_tone(float exposure, float saturation)
m_impl->set_tone(exposure, saturation);
}
void Viewer::set_clipping_plane(const std::array<float, 4>& plane)
{
m_impl->set_clipping_plane(plane);
}
EViewType Viewer::get_view_type() const
{
return m_impl->get_view_type();
+6
View File
@@ -773,6 +773,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
m_uni_segments_exposure_id = glGetUniformLocation(m_segments_shader_id, "exposure");
m_uni_segments_saturation_id = glGetUniformLocation(m_segments_shader_id, "saturation");
m_uni_segments_bias_scale_id = glGetUniformLocation(m_segments_shader_id, "bias_scale");
// ORCA: section view
m_uni_segments_clipping_plane_id = glGetUniformLocation(m_segments_shader_id, "clipping_plane");
glcheck();
assert(m_uni_segments_view_matrix_id != -1 &&
m_uni_segments_projection_matrix_id != -1 &&
@@ -797,6 +799,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
m_uni_options_height_width_angle_tex_id = glGetUniformLocation(m_options_shader_id, "height_width_angle_tex");
m_uni_options_colors_tex_id = glGetUniformLocation(m_options_shader_id, "color_tex");
m_uni_options_segment_index_tex_id = glGetUniformLocation(m_options_shader_id, "segment_index_tex");
// ORCA: section view
m_uni_options_clipping_plane_id = glGetUniformLocation(m_options_shader_id, "clipping_plane");
glcheck();
assert(m_uni_options_view_matrix_id != -1 &&
m_uni_options_projection_matrix_id != -1 &&
@@ -2110,6 +2114,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
glsafe(glUniform1f(m_uni_segments_exposure_id, m_exposure));
glsafe(glUniform1f(m_uni_segments_saturation_id, m_saturation));
glsafe(glUniform1f(m_uni_segments_bias_scale_id, m_rendering_shadow_casters ? 0.0f : 1.0f));
glsafe(glUniform4fv(m_uni_segments_clipping_plane_id, 1, m_clipping_plane.data()));
glsafe(glDisable(GL_CULL_FACE));
@@ -2197,6 +2202,7 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
glsafe(glUniform1i(m_uni_options_segment_index_tex_id, 3));
glsafe(glUniformMatrix4fv(m_uni_options_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
glsafe(glUniformMatrix4fv(m_uni_options_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
glsafe(glUniform4fv(m_uni_options_clipping_plane_id, 1, m_clipping_plane.data()));
glsafe(glEnable(GL_CULL_FACE));
+7
View File
@@ -89,6 +89,8 @@ public:
// caller decides which of the two it varies with the realistic view setting.
//
void set_tone(float exposure, float saturation);
// ORCA: section view, see Viewer::set_clipping_plane()
void set_clipping_plane(const std::array<float, 4>& plane) { m_clipping_plane = plane; }
EViewType get_view_type() const { return m_settings.view_type; }
void set_view_type(EViewType type);
@@ -371,6 +373,7 @@ private:
int m_uni_segments_exposure_id{ -1 };
int m_uni_segments_saturation_id{ -1 };
int m_uni_segments_bias_scale_id{ -1 };
int m_uni_segments_clipping_plane_id{ -1 };
//
// Caches for OpenGL uniforms id for options shader
//
@@ -380,6 +383,7 @@ private:
int m_uni_options_height_width_angle_tex_id{ -1 };
int m_uni_options_colors_tex_id{ -1 };
int m_uni_options_segment_index_tex_id{ -1 };
int m_uni_options_clipping_plane_id{ -1 };
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
//
// Caches for OpenGL uniforms id for cog marker shader
@@ -530,6 +534,9 @@ private:
float m_exposure{ 1.0f };
float m_saturation{ 1.0f };
// ORCA: section view
std::array<float, 4> m_clipping_plane{ 0.0f, 0.0f, 0.0f, 1.0f };
void apply_pending_updates();
void update_view_full_range();
void update_color_ranges();
+9
View File
@@ -1782,6 +1782,15 @@ void GCodeViewer::set_tone(float exposure, float saturation)
m_viewer.set_tone(exposure, saturation);
}
void GCodeViewer::set_clipping_plane(const ClippingPlane& plane)
{
// Flipped to match ClippingPlane::distance().
const Vec3f normal = -plane.get_normal().cast<float>();
m_viewer.set_clipping_plane(plane.is_active() ?
std::array<float, 4>{ normal.x(), normal.y(), normal.z(), float(plane.get_offset()) } :
std::array<float, 4>{ 0.0f, 0.0f, 0.0f, 1.0f });
}
void GCodeViewer::render_overlay(int canvas_width, int canvas_height, int right_margin)
{
if (m_viewer.get_extrusion_roles().empty())
+2
View File
@@ -300,6 +300,8 @@ public:
// ORCA: tone applied to the shaded toolpaths, paying back the light the lighting term,
// the shadow and the SSAO pass each take off. 1.0/1.0 is a no-op.
void set_tone(float exposure, float saturation);
// ORCA: section view
void set_clipping_plane(const ClippingPlane& plane);
//BBS
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
+231 -68
View File
@@ -1324,8 +1324,11 @@ GLCanvas3D::~GLCanvas3D()
m_shadow_map_fbo = 0;
}
m_plate_shadow_mask.reset();
m_section_view_caps.clear();
}
m_plate_shadow_mask_key.clear();
if (m_section_view->owner == this)
m_section_view->owner = nullptr;
reset_volumes();
@@ -1474,6 +1477,7 @@ void GLCanvas3D::reset_volumes()
m_selection.clear();
m_volumes.clear();
m_section_view_caps.clear();
m_dirty = true;
_set_warning_notification(EWarning::ObjectOutside, false);
@@ -1624,6 +1628,42 @@ ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(ObjectFil
return state;
}
void GLCanvas3D::set_section_view_ratio(double ratio)
{
ratio = std::clamp(ratio, 0., 1.);
if (ratio == m_section_view->ratio)
return;
if (ratio == 0.)
m_section_view->last_ratio = m_section_view->ratio;
m_section_view->ratio = ratio;
// Only the first cut faces the camera; later ones keep their angle until it is set again.
if (m_section_view->normal.isZero())
align_section_view_to_camera();
else
_on_section_view_changed();
}
void GLCanvas3D::toggle_section_view()
{
const double last_ratio = m_section_view->last_ratio;
set_section_view_ratio(is_section_view_active() ? 0. : last_ratio > 0. ? last_ratio : 0.5);
}
void GLCanvas3D::align_section_view_to_camera()
{
m_section_view->normal = -wxGetApp().plater()->get_camera().get_dir_forward();
_on_section_view_changed();
}
void GLCanvas3D::_on_section_view_changed()
{
m_gizmos.update_section_view();
// Set from the overlay, after this frame's scene was drawn.
set_as_dirty();
request_extra_frame();
}
void GLCanvas3D::toggle_world_axes_visibility(bool force_show)
{
if (force_show) {
@@ -2339,6 +2379,8 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
}
/* preview render */
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
// Before the shadow pass, so the cut away part casts no shadow.
m_gcode_viewer.set_clipping_plane(_get_section_view_plane());
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
_render_sla_slices();
_render_selection();
@@ -3121,7 +3163,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Bed);
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
m_gizmos.update_data();
m_gizmos.update_assemble_view_data();
m_gizmos.refresh_on_off_state();
// Update the toolbar
@@ -3837,12 +3878,18 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
m_key_down = { keyCode, key_repeats(keyCode) };
else if (evt.GetEventType() == wxEVT_KEY_UP)
key_released(keyCode);
const bool alt_wheel_released = evt.GetEventType() == wxEVT_KEY_UP && keyCode == WXK_ALT && std::exchange(m_alt_wheel_used, false);
auto imgui = wxGetApp().imgui();
if (imgui->update_key_data(evt))
render();
else
{
// Before the gizmos and the selection, which Esc also closes.
if (evt.GetEventType() == wxEVT_KEY_DOWN && keyCode == WXK_ESCAPE && std::exchange(m_section_view->panel_open, false)) {
_set_overlay_as_dirty();
return;
}
if (!m_gizmos.on_key(evt)) {
if (evt.GetEventType() == wxEVT_KEY_UP) {
if (evt.ShiftDown() && evt.ControlDown() && keyCode == WXK_SPACE) {
@@ -3911,7 +3958,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
&& keyCode != WXK_LEFT
&& keyCode != WXK_UP
&& keyCode != WXK_RIGHT
&& keyCode != WXK_DOWN) {
&& keyCode != WXK_DOWN
&& !alt_wheel_released) {
evt.Skip(); // Needed to have EVT_CHAR generated as well
}
}
@@ -3931,6 +3979,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
if (evt.MiddleIsDown())
return;
m_alt_wheel_used |= evt.AltDown();
#if ENABLE_RETINA_GL
const float scale = m_retina_helper->get_scale_factor();
evt.SetX(evt.GetX() * scale);
@@ -3976,25 +4026,21 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
if (m_gizmos.on_mouse_wheel(evt))
return;
if (m_canvas_type == CanvasAssembleView && (evt.AltDown() || evt.CmdDown()) && m_gizmos.m_assemble_view_data != nullptr) {
if (evt.AltDown()) {
set_section_view_ratio(get_section_view_ratio() + (evt.GetWheelRotation() < 0 ? -0.01 : 0.01));
return;
}
if (m_canvas_type == CanvasAssembleView && evt.CmdDown()) {
float rotation = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
if (evt.AltDown()) {
auto clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position();
clp_dist = rotation < 0.f
? std::max(0., clp_dist - 0.01)
: std::min(1., clp_dist + 0.01);
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true);
}
else if (evt.CmdDown()) {
m_explosion_ratio = rotation < 0.f
? std::max(1., m_explosion_ratio - 0.01)
: std::min(3., m_explosion_ratio + 0.01);
if (m_explosion_ratio != GLVolume::explosion_ratio) {
for (GLVolume* volume : m_volumes.volumes) {
volume->set_bounding_boxes_as_dirty();
}
GLVolume::explosion_ratio = m_explosion_ratio;
m_explosion_ratio = rotation < 0.f
? std::max(1., m_explosion_ratio - 0.01)
: std::min(3., m_explosion_ratio + 0.01);
if (m_explosion_ratio != GLVolume::explosion_ratio) {
for (GLVolume* volume : m_volumes.volumes) {
volume->set_bounding_boxes_as_dirty();
}
GLVolume::explosion_ratio = m_explosion_ratio;
}
return;
}
@@ -8556,7 +8602,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
glsafe(::glEnable(GL_DEPTH_TEST));
m_camera_clipping_plane = m_gizmos.get_clipping_plane();
m_camera_clipping_plane = _get_volumes_clipping_plane();
if (m_picking_enabled)
// Update the layer editing selection to the first object selected, update the current object maximum Z.
@@ -8599,10 +8645,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
GLGizmosManager& gm = get_gizmos_manager();
GLGizmoBase* current_gizmo = gm.get_current();
if (m_canvas_type == CanvasAssembleView) {
m_volumes.set_clipping_plane(m_gizmos.get_assemble_view_clipping_plane().get_data());
}
else if (current_gizmo && !current_gizmo->apply_clipping_plane()) {
if (current_gizmo && !current_gizmo->apply_clipping_plane()) {
m_volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data());
}
else {
@@ -8711,6 +8754,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
}
}
_render_section_view_caps();
break;
}
case GLVolumeCollection::ERenderType::Transparent:
@@ -8732,12 +8776,6 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
}
},
partly_inside_enable, printable_heights);
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
const GLGizmosManager& gm = get_gizmos_manager();
shader->stop_using();
gm.render_painter_assemble_view();
shader->start_using();
}
break;
}
}
@@ -8758,6 +8796,42 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
}
void GLCanvas3D::_render_section_view_caps()
{
// A gizmo with its own clipper draws its own cut faces.
const ClippingPlane plane = m_gizmos.get_clipping_plane().has_value() ? ClippingPlane::ClipsNothing() : _get_section_view_plane();
if (!plane.is_active() || m_model == nullptr) {
m_section_view_caps.clear();
return;
}
// Last frame's clippers are reused, so unchanged cuts are not recomputed.
std::map<const GLVolume*, MeshClipper> caps;
for (const GLVolume* volume : m_volumes.volumes) {
if (!volume->is_active || volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0 ||
volume->object_idx() >= int(m_model->objects.size()))
continue;
const ModelObject* object = m_model->objects[volume->object_idx()];
if (volume->volume_idx() >= int(object->volumes.size()))
continue;
// Skip volumes the plane misses.
const BoundingBoxf3& box = volume->transformed_bounding_box();
if (std::abs(plane.distance(box.center())) > 0.5 * box.size().dot(plane.get_normal().cwiseAbs()))
continue;
auto node = m_section_view_caps.extract(volume);
MeshClipper& clipper = node ? caps.insert(std::move(node)).position->second : caps[volume];
clipper.set_mesh(object->volumes[volume->volume_idx()]->mesh().its);
clipper.set_plane(plane);
clipper.set_transformation(Geometry::Transformation(volume->world_matrix()));
// No cut face below the plate.
if (m_canvas_type != CanvasAssembleView)
clipper.set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
clipper.render_cut({ 0.25f, 0.25f, 0.25f, 1.0f });
}
m_section_view_caps = std::move(caps);
}
bool GLCanvas3D::_is_xray_view_active() const
{
if (m_canvas_type == ECanvasType::CanvasPreview || !wxGetApp().plater()->is_show_xray())
@@ -9846,7 +9920,31 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding , padding); // without padding images clipping
imgui.begin(_L("Canvas Toolbar"), ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse);//
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollWithMouse);//
// Section view, on top so its panel opens above the toolbar.
const bool section_view = is_section_view_active();
ImTextureID s_normal_id = m_gizmos.get_icon_texture_id(section_view ?
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE_DARK : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE) :
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_DARK : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION));
ImTextureID s_hover_id = m_gizmos.get_icon_texture_id(section_view ?
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE_DARK_HOVER : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE_HOVER) :
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_DARK_HOVER : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_HOVER));
if (ImGui::ImageButton3(s_normal_id, s_hover_id, btn_size))
m_section_view->panel_open = !m_section_view->panel_open;
else if (ImGui::IsItemHovered()) {
if (const float wheel = ImGui::GetIO().MouseWheel; wheel != 0.f)
set_section_view_ratio(get_section_view_ratio() + (wheel < 0.f ? -0.01 : 0.01));
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
toggle_section_view();
else if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle))
align_section_view_to_camera();
imgui.tooltip(_L("Section view"), ImGui::GetFontSize() * 20.0f);
}
const ImVec2 section_panel_pos = ImGui::GetItemRectMin() - ImVec2(0.f, spacing.y);
ImGui::Dummy({ 0, spacing.y});
ImTextureID m_normal_id = m_gizmos.get_icon_texture_id(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU_DARK : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU);
ImTextureID m_hover_id = m_gizmos.get_icon_texture_id(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU_DARK_HOVER : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU_HOVER);
@@ -10012,6 +10110,76 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::PopStyleVar(6);
imgui.end();
_render_section_view_panel(section_panel_pos);
}
void GLCanvas3D::_render_section_view_panel(const ImVec2& bottom_left)
{
if (!m_section_view->panel_open)
return;
ImGuiWrapper& imgui = *wxGetApp().imgui();
const float sc = get_scale();
ImGuiWrapper::push_toolbar_style(sc);
ImGui::PushStyleColor(ImGuiCol_WindowBg, m_is_dark ? ImGuiWrapper::COL_TOOLBAR_BG_DARK : ImGuiWrapper::COL_TOOLBAR_BG);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.f * sc);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.f, 8.f) * sc);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.f, 4.f) * sc);
ImGui::SetNextWindowPos(bottom_left, ImGuiCond_Always, ImVec2(0.f, 1.f));
imgui.begin(std::string("##CanvasSectionView"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar);
const wxString alt = GUI::shortkey_alt_prefix();
const std::vector<std::pair<wxString, wxString>> tips = {
{ alt + _L("Mouse wheel"), _L("Move section plane") },
{ _L("Mouse wheel on icon"), _L("Move section plane") },
{ _L("Right click on icon"), _L("Toggle section view") },
{ _L("Middle click on icon"), _L("Set viewing angle") },
};
// render_tooltip_button() puts the tips below y plus the panel height; they go above the panel.
const ImGuiStyle& style = ImGui::GetStyle();
const float tips_h = tips.size() * (ImGui::GetTextLineHeight() + style.ItemSpacing.y) - style.ItemSpacing.y + 2.f * style.WindowPadding.y;
GLGizmoUtils::render_tooltip_button(&imgui, *this, tips, ImGui::GetWindowPos().x,
ImGui::GetWindowPos().y - style.ItemSpacing.y - tips_h - ImGui::GetContentRegionMax().y - ImGui::GetFrameHeight());
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
imgui.text(_L("Section view"));
float ratio = float(get_section_view_ratio());
ImGui::SameLine();
ImGui::SetNextItemWidth(imgui.scaled(7.f));
bool changed = imgui.bbl_slider_float_style("##section_view", &ratio, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine();
ImGui::SetNextItemWidth(1.5f * imgui.get_slider_icon_size().x);
changed |= ImGui::BBLDragFloat("##section_view_input", &ratio, 0.05f, 0.0f, 0.0f, "%.2f");
if (changed)
set_section_view_ratio(ratio);
ImGui::SameLine();
if (imgui.button(_L("Set viewing angle")))
align_section_view_to_camera();
imgui.disabled_begin(!is_section_view_active());
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.f, 0.f, 0.f, 0.f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.f, 0.f, 0.f, 0.f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f);
if (imgui.button(wxString(ImGui::RevertBtn) + "##section_view_reset", _L("Reset")))
set_section_view_ratio(0.);
ImGui::PopStyleVar();
ImGui::PopStyleColor(3);
imgui.disabled_end();
imgui.end();
ImGui::PopStyleVar(3);
ImGui::PopStyleColor();
ImGuiWrapper::pop_toolbar_style();
}
void GLCanvas3D::_render_separator_toolbar_right() const
@@ -10259,17 +10427,12 @@ float GLCanvas3D::_render_assembly_tooltip_button(ImGuiWrapper* imgui_wrapper) c
//BBS
void GLCanvas3D::_render_assemble_control()
{
if(m_gizmos.m_assemble_view_data == nullptr)
return;
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
GLVolume::explosion_ratio = m_explosion_ratio = 1.0;
return;
}
if (m_gizmos.get_current_type() == GLGizmosManager::EType::MmSegmentation) {
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(0.0, true);
if (m_gizmos.get_current_type() == GLGizmosManager::EType::MmSegmentation)
return;
}
ImGuiWrapper* imgui = wxGetApp().imgui();
@@ -10279,8 +10442,6 @@ void GLCanvas3D::_render_assemble_control()
auto canvas_h = float(get_canvas_size().get_height());
const float text_padding = 7.0f;
const float text_size_x = std::max(imgui->calc_text_size(_L("Reset direction")).x + 2 * ImGui::GetStyle().FramePadding.x,
std::max(imgui->calc_text_size(_L("Explosion Ratio")).x, imgui->calc_text_size(_L("Section View")).x));
const float slider_width = 60.0f;
const float value_size = imgui->calc_text_size("3.00"sv).x + text_padding * 2;
const float item_spacing = imgui->get_item_spacing().x;
@@ -10295,34 +10456,6 @@ void GLCanvas3D::_render_assemble_control()
tooltip_button_width = _render_assembly_tooltip_button(imgui);
}
float same_line_width = tooltip_button_width;
{
float clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position();
if (clp_dist == 0.f) {
ImGui::AlignTextToFramePadding();
imgui->text(_L("Section View"));
}
else {
if (imgui->button(_L("Reset direction"))) {
wxGetApp().CallAfter([this]() {
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(-1., false);
});
}
}
same_line_width += (text_size_x + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(slider_width);
bool view_slider_changed = imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
same_line_width += (slider_width + item_spacing);
ImGui::SameLine(same_line_width);
ImGui::PushItemWidth(value_size);
bool view_input_changed = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (view_slider_changed || view_input_changed)
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true);
same_line_width += (value_size + item_spacing * 2);
}
{
auto temp_x = imgui->calc_text_size(_L("Explosion Ratio")).x;
ImGui::SameLine(same_line_width);
@@ -10746,12 +10879,42 @@ Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos)
return mouse_ray(mouse_pos).intersect_plane(0.0);
}
ClippingPlane GLCanvas3D::_get_section_view_plane() const
{
const SectionView& section_view = *m_section_view;
if (section_view.ratio == 0.)
return ClippingPlane::ClipsNothing();
// The owner's objects, so Preview cuts where Prepare does.
const GLCanvas3D& owner = section_view.owner != nullptr ? *section_view.owner : *this;
BoundingBoxf3 box = owner.volumes_bounding_box(true);
if (!box.defined)
box = owner.volumes_bounding_box();
// G-code without objects: its toolpaths, merged by corner as a flat box is undefined.
if (!box.defined && m_gcode_viewer.has_data()) {
box.merge(m_gcode_viewer.get_paths_bounding_box().min);
box.merge(m_gcode_viewer.get_paths_bounding_box().max);
}
if (!box.defined)
return ClippingPlane::ClipsNothing();
// Sweeps the bounding sphere from the camera side (0) to the far side (1), as ObjectClipper does.
const double radius = box.radius();
return ClippingPlane(section_view.normal, section_view.normal.dot(box.center()) + radius - 2. * radius * section_view.ratio);
}
ClippingPlane GLCanvas3D::_get_volumes_clipping_plane() const
{
const std::optional<ClippingPlane> gizmo_plane = m_gizmos.get_clipping_plane();
return gizmo_plane.has_value() ? *gizmo_plane : _get_section_view_plane().inverted_normal();
}
ClippingPlane GLCanvas3D::get_raycaster_clipping_plane() const
{
// Orca: Ignore the gizmo clipping plane when the active tool does not apply it, and
// invert the result into the convention expected by SceneRaycaster.
GLGizmoBase* current_gizmo = m_gizmos.get_current();
return ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() :
return ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? _get_volumes_clipping_plane() :
ClippingPlane::ClipsNothing())
.inverted_normal();
}
+28
View File
@@ -619,6 +619,19 @@ private:
std::array<ClippingPlane, 2> m_clipping_planes;
ClippingPlane m_camera_clipping_plane;
bool m_use_clipping_planes;
struct SectionView
{
explicit SectionView(const GLCanvas3D* owner) : owner(owner) {}
double ratio{ 0. }; // 0 = off
double last_ratio{ 0. }; // before it was switched off
Vec3d normal{ Vec3d::Zero() }; // zero until first aimed
bool panel_open{ false };
const GLCanvas3D* owner; // whose objects place the plane
};
std::shared_ptr<SectionView> m_section_view{ std::make_shared<SectionView>(this) };
std::map<const GLVolume*, MeshClipper> m_section_view_caps;
// Its release would open the hidden window menu on Windows.
bool m_alt_wheel_used{ false };
std::array<SlaCap, 2> m_sla_caps;
std::string m_sidebar_field;
// when true renders an extra frame by not resetting m_dirty to false
@@ -943,6 +956,14 @@ public:
void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); }
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_volumes.set_color_clip_plane_colors(colors); }
bool is_section_view_active() const { return m_section_view->ratio > 0.; }
double get_section_view_ratio() const { return m_section_view->ratio; }
const Vec3d& get_section_view_normal() const { return m_section_view->normal; }
void set_section_view_ratio(double ratio);
void toggle_section_view();
void align_section_view_to_camera();
void share_section_view(const GLCanvas3D& owner) { m_section_view = owner.m_section_view; }
void toggle_world_axes_visibility(bool force_show = false);
void refresh_camera_scene_box();
void set_color_by(const std::string& value);
@@ -1392,6 +1413,7 @@ private:
void _render_cad_grid(const Transform3d& view_matrix, const Transform3d& projection_matrix);
//BBS: add outline drawing logic
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
void _render_section_view_caps();
void _render_wireframe_overlay();
bool _is_xray_view_active() const;
void _render_xray_volumes();
@@ -1418,6 +1440,7 @@ private:
void _render_assemble_view_toolbar() const;
void _render_return_toolbar() const;
void _render_canvas_toolbar();
void _render_section_view_panel(const ImVec2& bottom_left);
void _render_separator_toolbar_right() const;
void _render_separator_toolbar_left() const;
void _render_collapse_toolbar() const;
@@ -1449,6 +1472,11 @@ private:
Gesture
};
void _on_section_view_changed();
// In the ClippingPlane::is_point_clipped() convention.
ClippingPlane _get_section_view_plane() const;
// In the volume shaders' convention: the open gizmo's own plane, else the section view's.
ClippingPlane _get_volumes_clipping_plane() const;
// Orca: These helpers keep clipping, orbit pivots, and perspective-pan depth selection consistent.
ClippingPlane get_raycaster_clipping_plane() const;
bool is_bed_visible() const;
+2 -40
View File
@@ -85,7 +85,6 @@ bool GLGizmoBrimEars::on_init()
m_shortcut = Shortcut::GizmoBrimEars;
const wxString ctrl = GUI::shortkey_ctrl_prefix();
const wxString alt = GUI::shortkey_alt_prefix();
m_desc["brim_ear_radius"] = _L("Brim ear radius");
m_desc["max_angle"] = _L("Max angle");
@@ -96,13 +95,11 @@ bool GLGizmoBrimEars::on_init()
m_desc["create"] = _L("Create");
m_desc["auto_generate"] = _L("Auto-generate");
m_desc["auto-generate-tooltip"] = _L("Generate brim ears using Max angle and Detection radius");
m_desc["section_view"] = _L("Section view");
m_shortcuts = {
{_L("Left mouse button"), _L("Add or Select")},
{_L("Right mouse button"), _L("Remove")},
{ctrl + _L("Mouse wheel"), m_desc["brim_ear_radius"]},
{alt + _L("Mouse wheel"), m_desc["section_view"]},
};
return true;
@@ -551,26 +548,6 @@ bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_p
apply_radius_change();
}
if (action == SLAGizmoEventType::MouseWheelUp && alt_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::min(1., pos + 0.01);
m_c->object_clipper()->set_position_by_ratio(pos, false, true);
return true;
}
if (action == SLAGizmoEventType::MouseWheelDown && alt_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::max(0., pos - 0.01);
m_c->object_clipper()->set_position_by_ratio(pos, false, true);
return true;
}
// reset clipper position
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position_by_ratio(-1., false);
return true;
}
return false;
}
@@ -692,8 +669,8 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
float space_size = m_imgui->get_style_scaling() * 8;
std::vector<wxString> text_list = {m_desc["brim_ear_radius"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"],
m_desc["create"], m_desc["remove"]};
std::vector<wxString> text_list = {m_desc["brim_ear_radius"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["create"],
m_desc["remove"]};
float widest_text = m_imgui->find_widest_text(text_list);
float caption_size = widest_text + space_size + ImGui::GetStyle().WindowPadding.x;
float input_text_size = m_imgui->scaled(10.0f);
@@ -782,21 +759,6 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
}
}
m_imgui->disabled_end();
ImGui::Separator();
ImGui::AlignTextToFramePadding();
float clp_dist = float(m_c->object_clipper()->get_position());
m_imgui->text(m_desc["section_view"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##section_view", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##section_view_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) {
m_c->object_clipper()->set_position_by_ratio(clp_dist, false, true);
}
ImGui::Separator();
+4 -35
View File
@@ -112,36 +112,30 @@ bool GLGizmoFdmSupports::on_init()
m_desc["highlight_by_angle"] = _L("Highlight overhangs");
m_desc["tool_type"] = _L("Tool type");
m_desc["gap_fill"] = _L("Gap fill");
m_desc["reset_direction"] = _L("Reset direction");
m_desc["clipping_of_view"] = _L("Section view");
m_desc["cursor_size"] = _L("Brush size");
m_desc["smart_fill_angle"] = _L("Smart fill angle");
m_desc["gap_area"] = _L("Gap area");
const wxString ctrl = GUI::shortkey_ctrl_prefix();
const wxString alt = GUI::shortkey_alt_prefix();
const wxString shift = GUI::shortkey_shift_prefix();
std::pair<wxString, wxString> enforce_shortcut = {_L("Left mouse button"), _L("Enforce supports")};
std::pair<wxString, wxString> block_shortcut = {_L("Right mouse button"), _L("Block supports")};
std::pair<wxString, wxString> remove_shortcut = {shift + _L("Left mouse button"), _L("Erase")};
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
m_shortcuts_brush = {
enforce_shortcut,
block_shortcut,
remove_shortcut,
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
clipping_shortcut
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]}
};
m_shortcuts_bucket_fill = {
enforce_shortcut,
block_shortcut,
remove_shortcut,
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
clipping_shortcut
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]}
};
m_shortcuts_gap_fill = {
@@ -237,11 +231,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
// 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 space_size = m_imgui->get_style_scaling() * 8;
const float clipping_slider_left = m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
const float gap_fill_slider_left = m_imgui->calc_text_size(m_desc.at("gap_fill")).x + m_imgui->scaled(1.5f);
const float highlight_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle")).x + m_imgui->scaled(1.5f);
const float reset_button_slider_left = m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2;
const float on_overhangs_only_width = m_imgui->calc_text_size(m_desc["on_overhangs_only"]).x + m_imgui->scaled(1.5f);
const float filter_btn_width = m_imgui->calc_text_size(m_desc.at("perform")).x + m_imgui->scaled(1.5f);
const float gap_area_txt_width = m_imgui->calc_text_size(m_desc.at("gap_area")).x + m_imgui->scaled(1.5f);
@@ -254,14 +246,14 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
float caption_max = 0.f;
float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 5>{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) {
for (const auto &t : std::array<std::string, 4>{"enforce", "block", "remove", "cursor_size"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
}
total_text_max += caption_max + m_imgui->scaled(1.f);
caption_max += m_imgui->scaled(1.f);
const float sliders_left_width = std::max(gap_area_txt_width, std::max(smart_fill_angle_txt_width, std::max(reset_button_slider_left, std::max(std::max(cursor_slider_left, clipping_slider_left), std::max(highlight_slider_left, gap_fill_slider_left)))));
const float sliders_left_width = std::max(gap_area_txt_width, std::max(smart_fill_angle_txt_width, std::max(cursor_slider_left, std::max(highlight_slider_left, gap_fill_slider_left))));
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
@@ -458,29 +450,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##angle_threshold_deg_input", &m_highlight_by_angle_threshold_deg, 0.05f, 0.0f, 0.0f, "%.2f");
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(sliders_width);
bool b_bbl_slider_float = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_drag_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
ImGui::Separator();
render_tooltip_button(x, y);
+6 -41
View File
@@ -51,10 +51,8 @@ bool GLGizmoFuzzySkin::on_init()
m_shortcut = Shortcut::GizmoFuzzySkin;
const wxString ctrl = GUI::shortkey_ctrl_prefix();
const wxString alt = GUI::shortkey_alt_prefix();
const wxString shift = GUI::shortkey_shift_prefix();
m_desc["reset_direction"] = _L("Reset direction");
m_desc["remove_all"] = _L("Erase all");
m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere");
@@ -62,7 +60,6 @@ bool GLGizmoFuzzySkin::on_init()
m_desc["tool_type"] = _L("Tool type");
m_desc["tool_brush"] = _L("Brush");
m_desc["tool_smart_fill"] = _L("Smart fill");
m_desc["clipping_of_view"] = _L("Section view");
m_desc["cursor_size"] = _L("Brush size");
m_desc["add_fuzzy_skin"] = _L("Add fuzzy skin");
m_desc["remove_fuzzy_skin"] = _L("Remove fuzzy skin");
@@ -70,26 +67,22 @@ bool GLGizmoFuzzySkin::on_init()
std::pair<wxString, wxString> add_fuzzy_skin_shortcut = {_L("Left mouse button"), m_desc["add_fuzzy_skin"]};
std::pair<wxString, wxString> remove_fuzzy_skin_shortcut = {shift + _L("Left mouse button"), m_desc["remove_fuzzy_skin"]};
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
m_shortcuts_brush = {
add_fuzzy_skin_shortcut,
remove_fuzzy_skin_shortcut,
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
clipping_shortcut
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]}
};
m_shortcuts_triangle = {
add_fuzzy_skin_shortcut,
remove_fuzzy_skin_shortcut,
clipping_shortcut
remove_fuzzy_skin_shortcut
};
m_shortcuts_smart_fill = {
add_fuzzy_skin_shortcut,
remove_fuzzy_skin_shortcut,
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
clipping_shortcut
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]}
};
return true;
@@ -162,8 +155,6 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
// 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 space_size = m_imgui->get_style_scaling() * 8;
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f),
m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
@@ -187,9 +178,7 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
total_text_max += caption_max + m_imgui->scaled(1.f);
caption_max += m_imgui->scaled(1.f);
const float circle_max_width = std::max(clipping_slider_left, cursor_slider_left);
const float sliders_left_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
const float sliders_left_width = std::max(smart_fill_slider_left, cursor_slider_left);
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
const float empty_button_width = m_imgui->calc_button_size("").x;
@@ -259,10 +248,10 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(circle_max_width);
ImGui::SameLine(cursor_slider_left);
ImGui::PushItemWidth(sliders_width);
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width + circle_max_width);
ImGui::SameLine(drag_left_width + cursor_slider_left);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
} else if (m_current_tool == ImGui::TriangleButtonIcon) {
@@ -291,30 +280,6 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
ImGui::BBLDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f");
}
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
ImGui::Separator();
render_tooltip_button(x, y);
@@ -118,8 +118,6 @@ bool GLGizmoMmuSegmentation::on_init()
const wxString alt = GUI::shortkey_alt_prefix();
const wxString shift = GUI::shortkey_shift_prefix();
m_desc["clipping_of_view"] = _L("Section view");
m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Brush size");
m_desc["cursor_type"] = _L("Brush shape");
m_desc["paint"] = _L("Paint");
@@ -146,14 +144,12 @@ bool GLGizmoMmuSegmentation::on_init()
std::pair<wxString, wxString> paint_shortcut = {_L("Left mouse button"), m_desc["paint"]};
std::pair<wxString, wxString> erase_shortcut = {shift + _L("Left mouse button"), m_desc["erase"]};
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
std::pair<wxString, wxString> toggle_wireframe_shortcut = {alt + shift + _L_CONTEXT("Enter", "Keyboard Shortcut"), m_desc["toggle_wireframe"]};
m_shortcuts_brush = {
paint_shortcut,
erase_shortcut,
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
clipping_shortcut,
toggle_wireframe_shortcut
};
@@ -161,7 +157,6 @@ bool GLGizmoMmuSegmentation::on_init()
paint_shortcut,
erase_shortcut,
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
clipping_shortcut,
toggle_wireframe_shortcut
};
@@ -397,8 +392,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
// 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 space_size = m_imgui->get_style_scaling() * 8;
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f),
m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
const float edge_detect_slider_left = m_imgui->calc_text_size(m_desc.at("edge_detection")).x + m_imgui->scaled(1.f);
@@ -413,18 +406,15 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
float caption_max = 0.f;
float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 6>{"paint", "erase", "cursor_size", "smart_fill_angle", "height_range", "clipping_of_view"}) {
for (const auto &t : std::array<std::string, 5>{"paint", "erase", "cursor_size", "smart_fill_angle", "height_range"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
}
total_text_max += caption_max + m_imgui->scaled(1.f);
caption_max += m_imgui->scaled(1.f);
const float circle_max_width = std::max(clipping_slider_left,cursor_slider_left);
const float height_max_width = std::max(clipping_slider_left,height_range_slider_left);
const float sliders_left_width = std::max(smart_fill_slider_left,
std::max(cursor_slider_left, std::max(edge_detect_slider_left, std::max(gap_area_slider_left, std::max(height_range_slider_left,
clipping_slider_left))))) + space_size;
std::max(cursor_slider_left, std::max(edge_detect_slider_left, std::max(gap_area_slider_left, height_range_slider_left)))) + space_size;
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
const int max_filament_items_per_line = 8;
@@ -658,28 +648,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
}
}
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
} else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position_by_ratio(-1., false); });
}
}
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) {
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
}
ImGui::Separator();
render_tooltip_button(x, y);
@@ -750,20 +750,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
return true;
}
}
else if (alt_down) {
// BBS
double pos = m_c->object_clipper()->get_position();
pos = action == SLAGizmoEventType::MouseWheelDown
? std::max(0., pos - 0.01)
: std::min(1., pos + 0.01);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position_by_ratio(-1., false);
return true;
}
if (action == SLAGizmoEventType::LeftDown
+3 -32
View File
@@ -46,11 +46,8 @@ bool GLGizmoSeam::on_init()
m_shortcut = Shortcut::GizmoSeam;
const wxString ctrl = GUI::shortkey_ctrl_prefix();
const wxString alt = GUI::shortkey_alt_prefix();
const wxString shift = GUI::shortkey_shift_prefix();
m_desc["clipping_of_view"] = _L("Section view");
m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Brush size");
m_desc["tool_type"] = _L("Tool type");
m_desc["enforce"] = _L("Enforce seam");
@@ -64,8 +61,7 @@ bool GLGizmoSeam::on_init()
{_L("Left mouse button"), m_desc["enforce"]},
{_L("Right mouse button"), m_desc["block"]},
{shift + _L("Left mouse button"), m_desc["remove"]},
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
{alt + _L("Mouse wheel"), m_desc["clipping_of_view"]}
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]}
};
return true;
@@ -155,20 +151,17 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
// 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 space_size = m_imgui->get_style_scaling() * 8;
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 + ImGui::GetStyle().FramePadding.x * 2)
+ m_imgui->scaled(1.5f);
const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
const float empty_button_width = m_imgui->calc_button_size("").x;
float caption_max = 0.f;
float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 6>{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) {
for (const auto &t : std::array<std::string, 4>{"enforce", "block", "remove", "cursor_size"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
}
const float sliders_left_width = std::max(cursor_size_slider_left, clipping_slider_left);
const float sliders_left_width = cursor_size_slider_left;
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
const float sliders_width = m_imgui->scaled(7.0f);
@@ -242,28 +235,6 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
m_imgui->bbl_checkbox(_L("Vertical"), m_vertical_only);
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
ImGui::Separator();
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
+8 -180
View File
@@ -380,22 +380,19 @@ std::vector<Vec3d> ObjectClipper::point_per_contour() const
}
void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal, bool vertical_normal)
void ObjectClipper::set_position_by_ratio(double pos, const Vec3d& normal)
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
int active_inst = get_pool()->selection_info()->get_active_instance();
double z_shift = get_pool()->selection_info()->get_sla_shift();
Vec3d normal;
if(vertical_normal) {
normal = {0, 0, 1};
}else {
//Vec3d camera_dir = wxGetApp().plater()->get_camera().get_dir_forward();
//if (abs(camera_dir(0)) > EPSILON || abs(camera_dir(1)) > EPSILON)
// camera_dir(2) = 0;
normal = (keep_normal && m_clp) ? m_clp->get_normal() : /*-camera_dir;*/ -wxGetApp().plater()->get_camera().get_dir_forward();
if (pos == 0. || mo == nullptr || active_inst < 0) {
// No plane, so the raycasts are not clipped.
m_clp_ratio = 0.;
m_clp.reset();
get_pool()->get_canvas()->set_as_dirty();
return;
}
Vec3d center;
if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
@@ -410,9 +407,6 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal, bool ver
float dist = normal.dot(center);
if (pos < 0.)
pos = m_clp_ratio;
m_clp_ratio = pos;
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2 * m_active_inst_bb_radius)));
@@ -445,171 +439,5 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou
}
using namespace AssembleViewDataObjects;
AssembleViewDataPool::AssembleViewDataPool(GLCanvas3D* canvas)
: m_canvas(canvas)
{
using c = AssembleViewDataID;
m_data[c::ModelObjectsInfo].reset(new ModelObjectsInfo(this));
m_data[c::ModelObjectsClipper].reset(new ModelObjectsClipper(this));
}
void AssembleViewDataPool::update(AssembleViewDataID required)
{
assert(check_dependencies(required));
for (auto& [id, data] : m_data) {
if (int(required) & int(AssembleViewDataID(id)))
data->update();
else
if (data->is_valid())
data->release();
}
}
ModelObjectsInfo* AssembleViewDataPool::model_objects_info() const
{
ModelObjectsInfo* sel_info = dynamic_cast<ModelObjectsInfo*>(m_data.at(AssembleViewDataID::ModelObjectsInfo).get());
assert(sel_info);
return sel_info->is_valid() ? sel_info : nullptr;
}
ModelObjectsClipper* AssembleViewDataPool::model_objects_clipper() const
{
ModelObjectsClipper* oc = dynamic_cast<ModelObjectsClipper*>(m_data.at(AssembleViewDataID::ModelObjectsClipper).get());
// ObjectClipper is used from outside the gizmos to report current clipping plane.
// This function can be called when oc is nullptr.
return (oc && oc->is_valid()) ? oc : nullptr;
}
#ifndef NDEBUG
// Check the required resources one by one and return true if all
// dependencies are met.
bool AssembleViewDataPool::check_dependencies(AssembleViewDataID required) const
{
// This should iterate over currently required data. Each of them should
// be asked about its dependencies and it must check that all dependencies
// are also in required and before the current one.
for (auto& [id, data] : m_data) {
// in case we don't use this, the deps are irrelevant
if (!(int(required) & int(AssembleViewDataID(id))))
continue;
AssembleViewDataID deps = data->get_dependencies();
assert(int(deps) == (int(deps) & int(required)));
}
return true;
}
#endif // NDEBUG
void ModelObjectsInfo::on_update()
{
if (!get_pool()->get_canvas()->get_model()->objects.empty()) {
m_model_objects = get_pool()->get_canvas()->get_model()->objects;
}
else {
m_model_objects.clear();
}
}
void ModelObjectsInfo::on_release()
{
m_model_objects.clear();
}
//int ModelObjectsInfo::get_active_instance() const
//{
// const Selection& selection = get_pool()->get_canvas()->get_selection();
// return selection.get_instance_idx();
//}
void ModelObjectsClipper::on_update()
{
const ModelObjectPtrs model_objects = get_pool()->model_objects_info()->model_objects();
if (model_objects.empty())
return;
// which mesh should be cut?
std::vector<const TriangleMesh*> meshes;
if (meshes.empty())
for (auto mo : model_objects) {
for (const ModelVolume* mv : mo->volumes)
meshes.push_back(&mv->mesh());
}
if (meshes != m_old_meshes) {
m_clippers.clear();
for (const TriangleMesh* mesh : meshes) {
m_clippers.emplace_back(new MeshClipper);
m_clippers.back()->set_mesh(mesh->its);
}
m_old_meshes = meshes;
m_active_inst_bb_radius = get_pool()->get_canvas()->volumes_bounding_box().radius();
}
}
void ModelObjectsClipper::on_release()
{
m_clippers.clear();
m_old_meshes.clear();
m_clp.reset();
m_clp_ratio = 0.;
}
void ModelObjectsClipper::render_cut() const
{
if (m_clp_ratio == 0.)
return;
const ModelObjectPtrs model_objects = get_pool()->model_objects_info()->model_objects();
size_t clipper_id = 0;
for (const ModelObject* mo : model_objects) {
Geometry::Transformation assemble_objects_trafo = mo->instances[0]->get_assemble_transformation();
auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly();
for (const ModelVolume* mv : mo->volumes) {
Geometry::Transformation vol_trafo = mv->get_transformation();
Geometry::Transformation trafo = assemble_objects_trafo * vol_trafo;
trafo.set_offset(trafo.get_offset() + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0) + offset_to_assembly * (GLVolume::explosion_ratio - 1.0));
auto& clipper = m_clippers[clipper_id];
clipper->set_plane(*m_clp);
clipper->set_transformation(trafo);
// BBS
clipper->render_cut({0.25f, 0.25f, 0.25f, 1.0f});
++clipper_id;
}
}
}
void ModelObjectsClipper::set_position(double pos, bool keep_normal)
{
Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward();
const Vec3d& center = get_pool()->get_canvas()->volumes_bounding_box().center();
float dist = normal.dot(center);
if (pos < 0.)
pos = m_clp_ratio;
m_clp_ratio = pos;
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius * GLVolume::explosion_ratio) - m_clp_ratio * 2 * m_active_inst_bb_radius * GLVolume::explosion_ratio)));
get_pool()->get_canvas()->set_as_dirty();
}
} // namespace GUI
} // namespace Slic3r
+1 -122
View File
@@ -52,7 +52,6 @@ enum class SLAGizmoEventType : unsigned char {
class CommonGizmosDataBase;
class AssembleViewDataBase;
namespace CommonGizmosDataObjects {
class SelectionInfo;
class InstancesHider;
@@ -62,11 +61,6 @@ namespace CommonGizmosDataObjects {
class SupportsClipper;
}
namespace AssembleViewDataObjects {
class ModelObjectsInfo;
class ModelObjectsClipper;
}
// Some of the gizmos use the same data that need to be updated ocassionally.
// It is also desirable that the data are not recalculated when the gizmos
// are just switched, but on the other hand, they should be released when
@@ -245,7 +239,7 @@ public:
void set_position_to_init_layer();
const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const;
void render_cut(const std::vector<size_t>* ignore_idxs = nullptr) const;
void set_position_by_ratio(double pos, bool keep_normal, bool vertical_normal=false);
void set_position_by_ratio(double pos, const Vec3d& normal);
void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos);
void set_behavior(bool hide_clipped, bool fill_cut, double contour_width);
@@ -272,121 +266,6 @@ private:
} // namespace CommonGizmosDataObjects
enum class AssembleViewDataID {
None = 0,
ModelObjectsInfo = 1 << 0,
ModelObjectsClipper = 1 << 4,
};
class AssembleViewDataPool {
public:
AssembleViewDataPool(GLCanvas3D* canvas);
// Update all resources and release what is not used.
// Accepts a bitmask of currently required resources.
void update(AssembleViewDataID required);
// Getters for the data that need to be accessed from the gizmos directly.
AssembleViewDataObjects::ModelObjectsInfo* model_objects_info() const;
AssembleViewDataObjects::ModelObjectsClipper* model_objects_clipper() const;
GLCanvas3D* get_canvas() const { return m_canvas; }
private:
std::map<AssembleViewDataID, std::unique_ptr<AssembleViewDataBase>> m_data;
GLCanvas3D* m_canvas;
#ifndef NDEBUG
bool check_dependencies(AssembleViewDataID required) const;
#endif
};
// Base class for a wrapper object managing a single resource.
// Each of the enum values above (safe None) will have an object of this kind.
class AssembleViewDataBase {
public:
// Pass a backpointer to the pool, so the individual
// objects can communicate with one another.
explicit AssembleViewDataBase(AssembleViewDataPool* cgdp)
: m_common{ cgdp } {}
virtual ~AssembleViewDataBase() {}
// Update the resource.
void update() { on_update(); m_is_valid = true; }
// Release any data that are stored internally.
void release() { on_release(); m_is_valid = false; }
// Returns whether the resource is currently maintained.
bool is_valid() const { return m_is_valid; }
#ifndef NDEBUG
// Return a bitmask of all resources that this one relies on.
// The dependent resource must have higher ID than the one
// it depends on.
virtual AssembleViewDataID get_dependencies() const { return AssembleViewDataID::None; }
#endif // NDEBUG
protected:
virtual void on_release() = 0;
virtual void on_update() = 0;
AssembleViewDataPool* get_pool() const { return m_common; }
private:
bool m_is_valid = false;
AssembleViewDataPool* m_common = nullptr;
};
namespace AssembleViewDataObjects
{
class ModelObjectsInfo : public AssembleViewDataBase
{
public:
explicit ModelObjectsInfo(AssembleViewDataPool* cgdp)
: AssembleViewDataBase(cgdp) {}
ModelObjectPtrs model_objects() const { return m_model_objects; }
//int get_active_instance() const;
float get_sla_shift() const { return m_z_shift; }
protected:
void on_update() override;
void on_release() override;
private:
ModelObjectPtrs m_model_objects;
float m_z_shift = 0.f;
};
class ModelObjectsClipper : public AssembleViewDataBase
{
public:
explicit ModelObjectsClipper(AssembleViewDataPool* cgdp)
: AssembleViewDataBase(cgdp) {}
#ifndef NDEBUG
AssembleViewDataID get_dependencies() const override { return AssembleViewDataID::ModelObjectsInfo; }
#endif // NDEBUG
void set_position(double pos, bool keep_normal);
double get_position() const { return m_clp_ratio; }
ClippingPlane* get_clipping_plane() const { return m_clp.get(); }
void render_cut() const;
protected:
void on_update() override;
void on_release() override;
private:
std::vector<const TriangleMesh*> m_old_meshes;
std::vector<std::unique_ptr<MeshClipper>> m_clippers;
std::unique_ptr<ClippingPlane> m_clp;
double m_clp_ratio = 0.;
double m_active_inst_bb_radius = 0.;
};
}
} // namespace GUI
} // namespace Slic3r
+26 -32
View File
@@ -278,8 +278,6 @@ bool GLGizmosManager::init()
//m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++));
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
if(!m_assemble_view_data)
m_assemble_view_data.reset(new AssembleViewDataPool(&m_parent));
for (auto& gizmo : m_gizmos) {
if (! gizmo->init()) {
@@ -374,7 +372,17 @@ bool GLGizmosManager::init_icon_textures()
icon_list.insert(std::make_pair((int) IC_CANVAS_ZOOM_DARK_HOVER, texture_id));
else
return false;
for (const auto& [icon, name] : std::initializer_list<std::pair<MENU_ICON_NAME, const char*>>{
{ IC_CANVAS_SECTION, "canvas_section" }, { IC_CANVAS_SECTION_HOVER, "canvas_section_hover" },
{ IC_CANVAS_SECTION_DARK, "canvas_section_dark" }, { IC_CANVAS_SECTION_DARK_HOVER, "canvas_section_dark_hover" },
{ IC_CANVAS_SECTION_ACTIVE, "canvas_section_active" }, { IC_CANVAS_SECTION_ACTIVE_HOVER, "canvas_section_active_hover" },
{ IC_CANVAS_SECTION_ACTIVE_DARK, "canvas_section_active_dark" }, { IC_CANVAS_SECTION_ACTIVE_DARK_HOVER, "canvas_section_active_dark_hover" } }) {
if (!IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/" + name + ".svg", 72, 72, texture_id))
return false;
icon_list.insert(std::make_pair((int) icon, texture_id));
}
return true;
}
@@ -476,14 +484,17 @@ void GLGizmosManager::set_hover_id(int id)
m_gizmos[m_current]->set_hover_id(id);
}
void GLGizmosManager::update_assemble_view_data()
void GLGizmosManager::update_section_view()
{
if (m_assemble_view_data) {
if (!wxGetApp().plater()->get_assmeble_canvas3D()->get_wxglcanvas()->IsShown())
m_assemble_view_data->update(AssembleViewDataID(0));
else
m_assemble_view_data->update(AssembleViewDataID((int)AssembleViewDataID::ModelObjectsInfo | (int)AssembleViewDataID::ModelObjectsClipper));
}
if (m_current != FdmSupports && m_current != Seam && m_current != MmSegmentation && m_current != FuzzySkin && m_current != BrimEars)
return;
CommonGizmosDataObjects::ObjectClipper* clipper = m_common_gizmos_data ? m_common_gizmos_data->object_clipper() : nullptr;
if (clipper == nullptr)
return;
// Brim ears always cut horizontally.
clipper->set_position_by_ratio(m_parent.get_section_view_ratio(), m_current == BrimEars ? Vec3d::UnitZ() : m_parent.get_section_view_normal());
}
void GLGizmosManager::update_data()
@@ -496,6 +507,7 @@ void GLGizmosManager::update_data()
m_common_gizmos_data->update(get_current()
? get_current()->get_requirements()
: CommonGizmosDataID(0));
update_section_view();
if (m_current != Undefined) m_gizmos[m_current]->data_changed(m_serializing);
// Orca: hack: Fix issue that flatten gizmo faces not updated after reload from disk
@@ -609,11 +621,11 @@ bool GLGizmosManager::is_allow_select_all() {
return false;
}
ClippingPlane GLGizmosManager::get_clipping_plane() const
std::optional<ClippingPlane> GLGizmosManager::get_clipping_plane() const
{
if (! m_common_gizmos_data
|| ! m_common_gizmos_data->object_clipper()
|| m_common_gizmos_data->object_clipper()->get_position() == 0.)
if (! m_common_gizmos_data || ! m_common_gizmos_data->object_clipper())
return std::nullopt;
else if (m_common_gizmos_data->object_clipper()->get_position() == 0.)
return ClippingPlane::ClipsNothing();
else {
const ClippingPlane& clp = *m_common_gizmos_data->object_clipper()->get_clipping_plane();
@@ -621,18 +633,6 @@ ClippingPlane GLGizmosManager::get_clipping_plane() const
}
}
ClippingPlane GLGizmosManager::get_assemble_view_clipping_plane() const
{
if (!m_assemble_view_data
|| !m_assemble_view_data->model_objects_clipper()
|| m_assemble_view_data->model_objects_clipper()->get_position() == 0.)
return ClippingPlane::ClipsNothing();
else {
const ClippingPlane& clp = *m_assemble_view_data->model_objects_clipper()->get_clipping_plane();
return ClippingPlane(-clp.get_normal(), clp.get_data()[3]);
}
}
bool GLGizmosManager::wants_reslice_supports_on_undo() const
{
return false;
@@ -665,12 +665,6 @@ void GLGizmosManager::render_painter_gizmo()
gizmo->render_painter_gizmo();
}
void GLGizmosManager::render_painter_assemble_view() const
{
if (m_assemble_view_data && m_assemble_view_data->model_objects_clipper())
m_assemble_view_data->model_objects_clipper()->render_cut();
}
// The icon bar, drawn with GL.
void GLGizmosManager::render_overlay()
{
+13 -5
View File
@@ -22,6 +22,7 @@
#include <cereal/specialize.hpp>
#include <wx/timer.h>
#include <map>
#include <optional>
//BBS: GUI refactor: to support top layout
#define BBS_TOOLBAR_ON_TOP 1
@@ -181,7 +182,6 @@ private:
bool gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event);
public:
std::unique_ptr<AssembleViewDataPool> m_assemble_view_data;
enum MENU_ICON_NAME {
IC_TOOLBAR_RESET = 0,
IC_TOOLBAR_RESET_HOVER,
@@ -198,6 +198,14 @@ public:
IC_CANVAS_ZOOM_HOVER,
IC_CANVAS_ZOOM_DARK,
IC_CANVAS_ZOOM_DARK_HOVER,
IC_CANVAS_SECTION,
IC_CANVAS_SECTION_HOVER,
IC_CANVAS_SECTION_DARK,
IC_CANVAS_SECTION_DARK_HOVER,
IC_CANVAS_SECTION_ACTIVE,
IC_CANVAS_SECTION_ACTIVE_HOVER,
IC_CANVAS_SECTION_ACTIVE_DARK,
IC_CANVAS_SECTION_ACTIVE_DARK_HOVER,
};
explicit GLGizmosManager(GLCanvas3D& parent);
@@ -266,7 +274,8 @@ public:
/// Should be called when selection changed
/// </summary>
void update_data();
void update_assemble_view_data();
// Passes the canvas' section view to the object clipper of the painting and brim ears gizmos.
void update_section_view();
EType get_current_type() const { return m_current; }
GLGizmoBase* get_current() const;
@@ -297,8 +306,8 @@ public:
bool is_paint_gizmo();
bool is_allow_select_all();
ClippingPlane get_clipping_plane() const;
ClippingPlane get_assemble_view_clipping_plane() const;
// Empty when the open gizmo has no object clipper.
std::optional<ClippingPlane> get_clipping_plane() const;
bool wants_reslice_supports_on_undo() const;
bool is_in_editing_mode(bool error_notification = false) const;
@@ -307,7 +316,6 @@ public:
void on_change_color_mode(bool is_dark);
void render_current_gizmo() const;
void render_painter_gizmo();
void render_painter_assemble_view() const;
void render_overlay();
void render_overlay_input_window();
+2 -1
View File
@@ -196,13 +196,13 @@ void KBShortcutsDialog::fill_pages()
mouse(Section::Camera, _L("Middle mouse"), "middle_mouse_drag_action"),
mouse(Section::Camera, _L("Right mouse"), "right_mouse_drag_action"),
fixed(Section::Camera, { wheel }, L("Zoom View")),
fixed(Section::Display, { alt, wheel }, L("Move section plane")),
});
page(_L("Painting"), _L("Available while a painting gizmo is open: supports, seam, fuzzy skin or color painting."), ShortcutContext::Painting, {
fixed(Section::Gizmos, { esc }, L("Deselect All")),
fixed(Section::Gizmos, { shift, left_button }, L("Move: press to snap by 1mm")),
fixed(Section::PaintingTools, { ctrl, wheel }, L("Support/Color Painting: adjust pen radius")),
fixed(Section::PaintingTools, { alt, wheel }, L("Support/Color Painting: adjust section position")),
});
page(_L("Objects list"), _L("Available while the object list has focus."), ShortcutContext::ObjectList, {
@@ -216,6 +216,7 @@ void KBShortcutsDialog::fill_pages()
page(_L("Preview"), _L("Available while the 3D view on the Preview tab has focus."), ShortcutContext::Preview, {
fixed(Section::Sliders, { shift_ctrl, any_key }, L("Move slider 5x faster")),
fixed(Section::Sliders, { shift_ctrl, wheel }, L("Scroll slider 5x faster")),
fixed(Section::Display, { alt, wheel }, L("Move section plane")),
});
}
+1
View File
@@ -7891,6 +7891,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D = new View3D(panel_3d, bed, &model, config, &background_process);
//BBS: use partplater's gcode
preview = new Preview(panel_3d, bed, &model, config, &background_process, partplate_list.get_current_slice_result(), [this]() { schedule_background_process(); });
preview->get_canvas3d()->share_section_view(*view3D->get_canvas3d());
assemble_view = new AssembleView(panel_3d, bed, &model, config, &background_process);