Merge remote-tracking branch 'origin/master' into ys_search

This commit is contained in:
YuSanka 2020-04-16 11:46:29 +02:00
commit ae51f280b8
35 changed files with 3137 additions and 1984 deletions

View File

@ -1,13 +1,13 @@
Did you encounter an issue with using Slic3r? Fear not! This guide will help you to write a good bug report in just a few, simple steps. Did you encounter an issue with using PrusaSlicer? Fear not! This guide will help you to write a good bug report in just a few, simple steps.
There is a good chance that the issue, you have encountered, is already reported. Please check the [list of reported issues](https://github.com/alexrj/Slic3r/issues) before creating a new issue report. If you find an existing issue report, feel free to add further information to that report. There is a good chance that the issue, you have encountered, is already reported. Please check the [list of reported issues](https://github.com/prusa3d/PrusaSlicer/issues) before creating a new issue report. If you find an existing issue report, feel free to add further information to that report.
If possible, please include the following information when [reporting an issue](https://github.com/alexrj/Slic3r/issues/new): If possible, please include the following information when [reporting an issue](https://github.com/prusa3d/PrusaSlicer/issues/new):
* Slic3r version (See the about dialog for the version number. If running from git, please include the git commit ID from `git rev-parse HEAD` also.) * PrusaSlicer version (See the about dialog for the version number. If running from git, please include the git commit ID from `git rev-parse HEAD` also.)
* Operating system type + version * Operating system type + version
* Steps to reproduce the issue, including: * Steps to reproduce the issue, including:
* Command line parameters used, if any * Command line parameters used, if any
* Slic3r configuration file (Use ``Export Config...`` from the ``File`` menu - please don't export a bundle) * PrusaSlicer configuration file (Use ``Export Config...`` from the ``File`` menu - please don't export a bundle)
* Expected result * Expected result
* Actual result * Actual result
* Any error messages * Any error messages
@ -17,5 +17,5 @@ If possible, please include the following information when [reporting an issue](
Please make sure only to include one issue per report. If you encounter multiple, unrelated issues, please report them as such. Please make sure only to include one issue per report. If you encounter multiple, unrelated issues, please report them as such.
Simon Tatham has written an excellent on article on [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) which is well worth reading, although it is not specific to Slic3r. Simon Tatham has written an excellent on article on [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) which is well worth reading, although it is not specific to PrusaSlicer.

File diff suppressed because it is too large Load Diff

View File

@ -6360,7 +6360,7 @@ msgstr "Redimensionner"
#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:459 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:459
msgid "Scale factors" msgid "Scale factors"
msgstr "Facteurs de redimensionnement" msgstr "Échelle"
#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 #: src/slic3r/GUI/KBShortcutsDialog.cpp:196
msgid "" msgid ""

View File

@ -429,7 +429,7 @@ msgstr "서포트 생성"
#: src/slic3r/GUI/ConfigManipulation.cpp:200 #: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density." msgid "The %1% infill pattern is not supposed to work at 100%% density."
msgstr "%1% 채우기 패턴은 100% 밀도로 작동하도록 되어 있지 않습니다." msgstr "%1% 채우기 패턴은 100%% 밀도로 작동하도록 되어 있지 않습니다."
#: src/slic3r/GUI/ConfigManipulation.cpp:202 #: src/slic3r/GUI/ConfigManipulation.cpp:202
msgid "Shall I switch to rectilinear fill pattern?" msgid "Shall I switch to rectilinear fill pattern?"

View File

@ -31,17 +31,18 @@ avoid_crossing_perimeters = 0
bottom_fill_pattern = rectilinear bottom_fill_pattern = rectilinear
bridge_angle = 0 bridge_angle = 0
bridge_flow_ratio = 0.95 bridge_flow_ratio = 0.95
bridge_speed = 25 bridge_speed = 20
brim_width = 0 brim_width = 0
clip_multipart_objects = 1 clip_multipart_objects = 1
compatible_printers = compatible_printers =
complete_objects = 0 complete_objects = 0
dont_support_bridges = 1 dont_support_bridges = 1
elefant_foot_compensation = 0 elefant_foot_compensation = 0
ensure_vertical_shell_thickness = 1 ensure_vertical_shell_thickness = 1
external_fill_pattern = rectilinear external_fill_pattern = rectilinear
external_perimeters_first = 0 external_perimeters_first = 0
external_perimeter_extrusion_width = 0.45 external_perimeter_extrusion_width = 0.40
external_perimeter_speed = 25
extra_perimeters = 0 extra_perimeters = 0
extruder_clearance_height = 12 extruder_clearance_height = 12
extruder_clearance_radius = 45 extruder_clearance_radius = 45
@ -52,14 +53,14 @@ fill_pattern = grid
first_layer_extrusion_width = 0.42 first_layer_extrusion_width = 0.42
first_layer_height = 0.2 first_layer_height = 0.2
first_layer_speed = 20 first_layer_speed = 20
gap_fill_speed = 30 gap_fill_speed = 15
gcode_comments = 0 gcode_comments = 0
infill_every_layers = 1 infill_every_layers = 1
infill_extruder = 1 infill_extruder = 1
infill_extrusion_width = 0.45 infill_extrusion_width = 0.45
infill_first = 0 infill_first = 0
infill_only_where_needed = 0 infill_only_where_needed = 0
infill_overlap = 25% infill_overlap = 20%
interface_shells = 0 interface_shells = 0
max_print_speed = 60 max_print_speed = 60
max_volumetric_extrusion_rate_slope_negative = 0 max_volumetric_extrusion_rate_slope_negative = 0
@ -83,7 +84,7 @@ single_extruder_multi_material_priming = 0
skirts = 3 skirts = 3
skirt_distance = 2 skirt_distance = 2
skirt_height = 1 skirt_height = 1
small_perimeter_speed = 25 small_perimeter_speed = 15
solid_infill_below_area = 0 solid_infill_below_area = 0
solid_infill_every_layers = 0 solid_infill_every_layers = 0
solid_infill_extruder = 1 solid_infill_extruder = 1
@ -92,7 +93,7 @@ spiral_vase = 0
standby_temperature_delta = -5 standby_temperature_delta = -5
support_material = 0 support_material = 0
support_material_extruder = 0 support_material_extruder = 0
support_material_extrusion_width = 0.4 support_material_extrusion_width = 0.40
support_material_interface_extruder = 0 support_material_interface_extruder = 0
support_material_angle = 0 support_material_angle = 0
support_material_buildplate_only = 0 support_material_buildplate_only = 0
@ -110,8 +111,8 @@ support_material_threshold = 45
support_material_with_sheath = 0 support_material_with_sheath = 0
support_material_xy_spacing = 60% support_material_xy_spacing = 60%
thin_walls = 0 thin_walls = 0
top_infill_extrusion_width = 0.45 top_infill_extrusion_width = 0.40
top_solid_infill_speed = 40 top_solid_infill_speed = 20
travel_speed = 130 travel_speed = 130
wipe_tower = 0 wipe_tower = 0
wipe_tower_bridging = 10 wipe_tower_bridging = 10
@ -194,7 +195,7 @@ top_solid_layers = 7
[print:*0.15mm*] [print:*0.15mm*]
inherits = *common* inherits = *common*
external_perimeter_speed = 40 external_perimeter_speed = 25
infill_acceleration = 1100 infill_acceleration = 1100
infill_speed = 50 infill_speed = 50
layer_height = 0.15 layer_height = 0.15
@ -242,7 +243,7 @@ top_solid_layers = 4
inherits = *common* inherits = *common*
bottom_solid_layers = 4 bottom_solid_layers = 4
bridge_flow_ratio = 0.95 bridge_flow_ratio = 0.95
external_perimeter_speed = 40 external_perimeter_speed = 25
infill_acceleration = 1100 infill_acceleration = 1100
infill_speed = 60 infill_speed = 60
layer_height = 0.3 layer_height = 0.3
@ -334,7 +335,7 @@ inherits = *0.30mm*
# alias = 0.30mm ULTRADRAFT # alias = 0.30mm ULTRADRAFT
bottom_solid_layers = 3 bottom_solid_layers = 3
bridge_speed = 30 bridge_speed = 30
external_perimeter_speed = 35 external_perimeter_speed = 30
infill_acceleration = 1100 infill_acceleration = 1100
infill_speed = 55 infill_speed = 55
max_print_speed = 60 max_print_speed = 60

View File

@ -22,6 +22,12 @@ bed_model = ender3_bed.stl
bed_texture = ender3.svg bed_texture = ender3.svg
default_materials = Generic PLA @ENDER3; Generic PETG @ENDER3; Generic ABS @ENDER3; Prusament PLA @ENDER3; Prusament PETG @ENDER3 default_materials = Generic PLA @ENDER3; Generic PETG @ENDER3; Generic ABS @ENDER3; Prusament PLA @ENDER3; Prusament PETG @ENDER3
[printer_model:ENDER2]
name = Creality Ender-2
variants = 0.4
technology = FFF
default_materials = Generic PLA @ENDER3; Generic PETG @ENDER3; Generic ABS @ENDER3; Prusament PLA @ENDER3; Prusament PETG @ENDER3
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.
@ -423,3 +429,9 @@ default_print_profile = 0.20mm NORMAL
default_filament_profile = Creality PLA default_filament_profile = Creality PLA
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 ; home all\nG1 Z2 F240\nG1 X2 Y10 F3000\nG1 Z0.28 F240\nG92 E0.0\nG1 Y190 E15.0 F1500.0 ; intro line\nG1 X2.3 F5000\nG1 Y10 E30 F1200.0 ; intro line\nG92 E0.0 start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 ; home all\nG1 Z2 F240\nG1 X2 Y10 F3000\nG1 Z0.28 F240\nG92 E0.0\nG1 Y190 E15.0 F1500.0 ; intro line\nG1 X2.3 F5000\nG1 Y10 E30 F1200.0 ; intro line\nG92 E0.0
end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+10, max_print_height)} F600{endif} ; Move print head up\nG1 X0 Y200 F3000 ; present print\nM84 X Y E ; disable motors end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+10, max_print_height)} F600{endif} ; Move print head up\nG1 X0 Y200 F3000 ; present print\nM84 X Y E ; disable motors
[printer:Creality ENDER-2]
inherits = Creality ENDER-3
bed_shape = 0x0,150x0,150x150,0x150
printer_model = ENDER2
max_print_height = 200

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -62,10 +62,8 @@ void main()
float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
intensity.y = 0.0; vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
if (NdotL > 0.0)
intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied). // Perform the same lighting calculation for the 2nd light source (no specular applied).
NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);

View File

@ -32,10 +32,8 @@ void main()
float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
intensity.y = 0.0; vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
if (NdotL > 0.0)
intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular) // Perform the same lighting calculation for the 2nd light source (no specular)
NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);

View File

@ -303,8 +303,10 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
ret.m_t = double(hit.t); ret.m_t = double(hit.t);
ret.m_dir = dir; ret.m_dir = dir;
ret.m_source = s; ret.m_source = s;
if(!std::isinf(hit.t) && !std::isnan(hit.t)) if(!std::isinf(hit.t) && !std::isnan(hit.t)) {
ret.m_normal = this->normal_by_face_id(hit.id); ret.m_normal = this->normal_by_face_id(hit.id);
ret.m_face_id = hit.id;
}
return ret; return ret;
} }
@ -340,8 +342,10 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
outs.back().m_t = double(hit.t); outs.back().m_t = double(hit.t);
outs.back().m_dir = dir; outs.back().m_dir = dir;
outs.back().m_source = s; outs.back().m_source = s;
if(!std::isinf(hit.t) && !std::isnan(hit.t)) if(!std::isinf(hit.t) && !std::isnan(hit.t)) {
outs.back().m_normal = this->normal_by_face_id(hit.id); outs.back().m_normal = this->normal_by_face_id(hit.id);
outs.back().m_face_id = hit.id;
}
} }
return outs; return outs;

View File

@ -55,6 +55,7 @@ public:
class hit_result { class hit_result {
// m_t holds a distance from m_source to the intersection. // m_t holds a distance from m_source to the intersection.
double m_t = infty(); double m_t = infty();
int m_face_id = -1;
const EigenMesh3D *m_mesh = nullptr; const EigenMesh3D *m_mesh = nullptr;
Vec3d m_dir; Vec3d m_dir;
Vec3d m_source; Vec3d m_source;
@ -74,6 +75,7 @@ public:
inline const Vec3d& direction() const { return m_dir; } inline const Vec3d& direction() const { return m_dir; }
inline const Vec3d& source() const { return m_source; } inline const Vec3d& source() const { return m_source; }
inline Vec3d position() const { return m_source + m_dir * m_t; } inline Vec3d position() const { return m_source + m_dir * m_t; }
inline int face() const { return m_face_id; }
inline bool is_valid() const { return m_mesh != nullptr; } inline bool is_valid() const { return m_mesh != nullptr; }
inline bool is_hit() const { return !std::isinf(m_t); } inline bool is_hit() const { return !std::isinf(m_t); }

View File

@ -31,9 +31,10 @@ set(SLIC3R_GUI_SOURCES
GUI/GLCanvas3DManager.cpp GUI/GLCanvas3DManager.cpp
GUI/Selection.hpp GUI/Selection.hpp
GUI/Selection.cpp GUI/Selection.cpp
GUI/Gizmos/GLGizmos.hpp
GUI/Gizmos/GLGizmosManager.cpp GUI/Gizmos/GLGizmosManager.cpp
GUI/Gizmos/GLGizmosManager.hpp GUI/Gizmos/GLGizmosManager.hpp
GUI/Gizmos/GLGizmosCommon.cpp
GUI/Gizmos/GLGizmosCommon.hpp
GUI/Gizmos/GLGizmoBase.cpp GUI/Gizmos/GLGizmoBase.cpp
GUI/Gizmos/GLGizmoBase.hpp GUI/Gizmos/GLGizmoBase.hpp
GUI/Gizmos/GLGizmoMove.cpp GUI/Gizmos/GLGizmoMove.cpp
@ -44,6 +45,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoScale.hpp GUI/Gizmos/GLGizmoScale.hpp
GUI/Gizmos/GLGizmoSlaSupports.cpp GUI/Gizmos/GLGizmoSlaSupports.cpp
GUI/Gizmos/GLGizmoSlaSupports.hpp GUI/Gizmos/GLGizmoSlaSupports.hpp
GUI/Gizmos/GLGizmoFdmSupports.cpp
GUI/Gizmos/GLGizmoFdmSupports.hpp
GUI/Gizmos/GLGizmoFlatten.cpp GUI/Gizmos/GLGizmoFlatten.cpp
GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoFlatten.hpp
GUI/Gizmos/GLGizmoCut.cpp GUI/Gizmos/GLGizmoCut.cpp

View File

@ -60,7 +60,7 @@ public:
boost::trim_all(key_trimmed); boost::trim_all(key_trimmed);
assert(key_trimmed == key); assert(key_trimmed == key);
assert(! key_trimmed.empty()); assert(! key_trimmed.empty());
#endif _NDEBUG #endif // _NDEBUG
std::string &old = m_storage[section][key]; std::string &old = m_storage[section][key];
if (old != value) { if (old != value) {
old = value; old = value;

View File

@ -1293,9 +1293,13 @@ void PointCtrl::set_value(const boost::any& value, bool change_event)
boost::any& PointCtrl::get_value() boost::any& PointCtrl::get_value()
{ {
double x, y; double x, y;
x_textctrl->GetValue().ToDouble(&x); if (!x_textctrl->GetValue().ToDouble(&x) ||
y_textctrl->GetValue().ToDouble(&y); !y_textctrl->GetValue().ToDouble(&y))
{
set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true);
show_error(m_parent, _L("Invalid numeric input."));
}
else
if (m_opt.min > x || x > m_opt.max || if (m_opt.min > x || x > m_opt.max ||
m_opt.min > y || y > m_opt.max) m_opt.min > y || y > m_opt.max)
{ {
@ -1305,7 +1309,7 @@ boost::any& PointCtrl::get_value()
if (y > m_opt.max) y = m_opt.max; if (y > m_opt.max) y = m_opt.max;
set_value(Vec2d(x, y), true); set_value(Vec2d(x, y), true);
show_error(m_parent, _(L("Input value is out of range"))); show_error(m_parent, _L("Input value is out of range"));
} }
return m_value = Vec2d(x, y); return m_value = Vec2d(x, y);

View File

@ -1,5 +1,4 @@
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "admesh/stl.h" #include "admesh/stl.h"
@ -1755,10 +1754,15 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObje
void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx)
{ {
for (GLVolume* vol : m_volumes.volumes) { for (GLVolume* vol : m_volumes.volumes) {
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) if (vol->composite_id.object_id == 1000) { // wipe tower
&& (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)) { vol->is_active = (visible && mo == nullptr);
vol->is_active = visible; }
vol->force_native_color = (instance_idx != -1); else {
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo)
&& (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)) {
vol->is_active = visible;
vol->force_native_color = (instance_idx != -1);
}
} }
} }
if (visible && !mo) if (visible && !mo)
@ -5304,7 +5308,7 @@ void GLCanvas3D::_picking_pass() const
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); m_camera_clipping_plane = m_gizmos.get_clipping_plane();
if (m_camera_clipping_plane.is_active()) { if (m_camera_clipping_plane.is_active()) {
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
::glEnable(GL_CLIP_PLANE0); ::glEnable(GL_CLIP_PLANE0);
@ -5473,7 +5477,7 @@ void GLCanvas3D::_render_objects() const
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); m_camera_clipping_plane = m_gizmos.get_clipping_plane();
if (m_picking_enabled) if (m_picking_enabled)
{ {

View File

@ -925,7 +925,8 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
local_menu->AppendSeparator(); local_menu->AppendSeparator();
auto mode_menu = new wxMenu(); auto mode_menu = new wxMenu();
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode"))); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode")));
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode"))); // mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode")));
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), _L("Advanced View Mode"));
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("Expert")), _(L("Expert View Mode"))); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("Expert")), _(L("Expert View Mode")));
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple);
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced);

View File

@ -32,6 +32,8 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
class ImGuiWrapper; class ImGuiWrapper;
class GLCanvas3D; class GLCanvas3D;
class ClippingPlane; class ClippingPlane;
enum class CommonGizmosDataID;
class CommonGizmosDataPool;
class GLGizmoBase class GLGizmoBase
{ {
@ -101,6 +103,7 @@ protected:
ImGuiWrapper* m_imgui; ImGuiWrapper* m_imgui;
bool m_first_input_window_render; bool m_first_input_window_render;
mutable std::string m_tooltip; mutable std::string m_tooltip;
CommonGizmosDataPool* m_c;
public: public:
GLGizmoBase(GLCanvas3D& parent, GLGizmoBase(GLCanvas3D& parent,
@ -128,6 +131,8 @@ public:
bool is_activable() const { return on_is_activable(); } bool is_activable() const { return on_is_activable(); }
bool is_selectable() const { return on_is_selectable(); } bool is_selectable() const { return on_is_selectable(); }
CommonGizmosDataID get_requirements() const { return on_get_requirements(); }
void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; }
unsigned int get_sprite_id() const { return m_sprite_id; } unsigned int get_sprite_id() const { return m_sprite_id; }
@ -161,6 +166,7 @@ protected:
virtual void on_set_hover_id() {} virtual void on_set_hover_id() {}
virtual bool on_is_activable() const { return true; } virtual bool on_is_activable() const { return true; }
virtual bool on_is_selectable() const { return true; } virtual bool on_is_selectable() const { return true; }
virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); }
virtual void on_enable_grabber(unsigned int id) {} virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {} virtual void on_disable_grabber(unsigned int id) {}
virtual void on_start_dragging() {} virtual void on_start_dragging() {}

View File

@ -0,0 +1,629 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoFdmSupports.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include <GL/glew.h>
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/GUI/Camera.hpp"
namespace Slic3r {
namespace GUI {
GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_quadric(nullptr)
{
m_clipping_plane.reset(new ClippingPlane());
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
// using GLU_FILL does not work when the instance's transformation
// contains mirroring (normals are reverted)
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
}
GLGizmoFdmSupports::~GLGizmoFdmSupports()
{
if (m_quadric != nullptr)
::gluDeleteQuadric(m_quadric);
}
bool GLGizmoFdmSupports::on_init()
{
m_shortcut_key = WXK_CONTROL_L;
m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Cursor size") + ": ";
m_desc["enforce_caption"] = _L("Left mouse button") + ": ";
m_desc["enforce"] = _L("Enforce supports");
m_desc["block_caption"] = _L("Right mouse button") + " ";
m_desc["block"] = _L("Block supports");
m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": ";
m_desc["remove"] = _L("Remove selection");
return true;
}
void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const Selection& selection)
{
const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr;
if (! mo)
return;
if (mo && selection.is_from_single_instance()
&& (mo != m_old_mo || mo->volumes.size() != m_old_volumes_size))
{
update_mesh();
m_old_mo = mo;
m_old_volumes_size = mo->volumes.size();
}
}
void GLGizmoFdmSupports::on_render() const
{
const Selection& selection = m_parent.get_selection();
glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
render_triangles(selection);
m_c->object_clipper()->render_cut();
render_cursor_circle();
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
{
const ModelObject* mo = m_c->selection_info()->model_object();
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } );
glsafe(::glPolygonOffset(-1.0, 1.0));
int mesh_id = -1;
for (const ModelVolume* mv : mo->volumes) {
if (! mv->is_model_part())
continue;
++mesh_id;
const Transform3d trafo_matrix =
mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
mv->get_matrix();
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(trafo_matrix.data()));
glsafe(::glColor4f(0.2f, 0.2f, 1.0f, 0.5f));
m_ivas[mesh_id][0].render();
glsafe(::glColor4f(1.f, 0.2f, 0.2f, 0.5f));
m_ivas[mesh_id][1].render();
glsafe(::glPopMatrix());
}
}
void GLGizmoFdmSupports::render_cursor_circle() const
{
const Camera& camera = wxGetApp().plater()->get_camera();
float zoom = (float)camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = m_parent.get_canvas_size();
float cnv_half_width = 0.5f * (float)cnv_size.get_width();
float cnv_half_height = 0.5f * (float)cnv_size.get_height();
if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f))
return;
Vec2d mouse_pos(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1));
Vec2d center(mouse_pos(0) - cnv_half_width, cnv_half_height - mouse_pos(1));
center = center * inv_zoom;
glsafe(::glLineWidth(1.5f));
float color[3];
color[0] = 0.f;
color[1] = 1.f;
color[2] = 0.3f;
glsafe(::glColor3fv(color));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
// ensure that the circle is renderered inside the frustrum
glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5)));
// ensure that the overlay fits the frustrum near z plane
double gui_scale = camera.get_gui_scale();
glsafe(::glScaled(gui_scale, gui_scale, 1.0));
glsafe(::glPushAttrib(GL_ENABLE_BIT));
glsafe(::glLineStipple(4, 0xAAAA));
glsafe(::glEnable(GL_LINE_STIPPLE));
::glBegin(GL_LINE_LOOP);
for (double angle=0; angle<2*M_PI; angle+=M_PI/20.)
::glVertex2f(GLfloat(center.x()+m_cursor_radius*cos(angle)), GLfloat(center.y()+m_cursor_radius*sin(angle)));
glsafe(::glEnd());
glsafe(::glPopAttrib());
glsafe(::glPopMatrix());
}
void GLGizmoFdmSupports::on_render_for_picking() const
{
}
void GLGizmoFdmSupports::update_mesh()
{
wxBusyCursor wait;
const ModelObject* mo = m_c->selection_info()->model_object();
size_t num_of_volumes = 0;
for (const ModelVolume* mv : mo->volumes)
if (mv->is_model_part())
++num_of_volumes;
m_selected_facets.resize(num_of_volumes);
m_neighbors.resize(num_of_volumes);
m_ivas.clear();
m_ivas.resize(num_of_volumes);
int volume_id = -1;
for (const ModelVolume* mv : mo->volumes) {
if (! mv->is_model_part())
continue;
++volume_id;
// This mesh does not account for the possible Z up SLA offset.
const TriangleMesh* mesh = &mv->mesh();
m_selected_facets[volume_id].assign(mesh->its.indices.size(), SelType::NONE);
m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
// Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets
for (size_t i=0; i<mesh->its.indices.size(); ++i) {
const stl_triangle_vertex_indices& ind = mesh->its.indices[i];
m_neighbors[volume_id][3*i] = std::make_pair(ind(0), i);
m_neighbors[volume_id][3*i+1] = std::make_pair(ind(1), i);
m_neighbors[volume_id][3*i+2] = std::make_pair(ind(2), i);
}
std::sort(m_neighbors[volume_id].begin(), m_neighbors[volume_id].end());
}
}
bool operator<(const GLGizmoFdmSupports::NeighborData& a, const GLGizmoFdmSupports::NeighborData& b) {
return a.first < b.first;
}
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo
// concludes that the event was not intended for it, it should return false.
bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{
if (action == SLAGizmoEventType::MouseWheelUp
|| action == SLAGizmoEventType::MouseWheelDown) {
if (control_down) {
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(pos, true);
return true;
}
else if (alt_down) {
m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown
? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin)
: std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax);
m_parent.set_as_dirty();
return true;
}
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position(-1., false);
return true;
}
if (action == SLAGizmoEventType::LeftDown
|| action == SLAGizmoEventType::RightDown
|| (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) {
SelType new_state = SelType::NONE;
if (! shift_down) {
if (action == SLAGizmoEventType::Dragging)
new_state = m_button_down == Button::Left
? SelType::ENFORCER
: SelType::BLOCKER;
else
new_state = action == SLAGizmoEventType::LeftDown
? SelType::ENFORCER
: SelType::BLOCKER;
}
const Camera& camera = wxGetApp().plater()->get_camera();
const Selection& selection = m_parent.get_selection();
const ModelObject* mo = m_c->selection_info()->model_object();
const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
const Transform3d& instance_trafo = mi->get_transformation().get_matrix();
std::vector<std::vector<std::pair<Vec3f, size_t>>> hit_positions_and_facet_ids;
bool some_mesh_was_hit = false;
Vec3f normal = Vec3f::Zero();
Vec3f hit = Vec3f::Zero();
size_t facet = 0;
Vec3f closest_hit = Vec3f::Zero();
double closest_hit_squared_distance = std::numeric_limits<double>::max();
size_t closest_facet = 0;
size_t closest_hit_mesh_id = size_t(-1);
// Transformations of individual meshes
std::vector<Transform3d> trafo_matrices;
int mesh_id = -1;
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
for (const ModelVolume* mv : mo->volumes) {
if (! mv->is_model_part())
continue;
++mesh_id;
trafo_matrices.push_back(instance_trafo * mv->get_matrix());
hit_positions_and_facet_ids.push_back(std::vector<std::pair<Vec3f, size_t>>());
if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
mouse_position,
trafo_matrices[mesh_id],
camera,
hit,
normal,
m_clipping_plane.get(),
&facet))
{
// Is this hit the closest to the camera so far?
double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
if (hit_squared_distance < closest_hit_squared_distance) {
closest_hit_squared_distance = hit_squared_distance;
closest_facet = facet;
closest_hit_mesh_id = mesh_id;
closest_hit = hit;
}
}
}
// We now know where the ray hit, let's save it and cast another ray
if (closest_hit_mesh_id != size_t(-1)) // only if there is at least one hit
hit_positions_and_facet_ids[closest_hit_mesh_id].emplace_back(closest_hit, closest_facet);
// Now propagate the hits
mesh_id = -1;
for (const ModelVolume* mv : mo->volumes) {
if (! mv->is_model_part())
continue;
++mesh_id;
bool update_both = false;
const Transform3d& trafo_matrix = trafo_matrices[mesh_id];
// Calculate how far can a point be from the line (in mesh coords).
// FIXME: The scaling of the mesh can be non-uniform.
const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor();
const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.;
const float limit = pow(m_cursor_radius/avg_scaling , 2.f);
// For all hits on this mesh...
for (const std::pair<Vec3f, size_t>& hit_and_facet : hit_positions_and_facet_ids[mesh_id]) {
some_mesh_was_hit = true;
const TriangleMesh* mesh = &mv->mesh();
std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
// Calculate direction from camera to the hit (in mesh coords):
Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast<float>() - hit_and_facet.first).normalized();
// A lambda to calculate distance from the centerline:
auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f point) -> float {
Vec3f diff = hit_and_facet.first - point;
return (diff - diff.dot(dir) * dir).squaredNorm();
};
// A lambda to determine whether this facet is potentionally visible (still can be obscured)
auto faces_camera = [&dir](const ModelVolume* mv, const size_t& facet) -> bool {
return (mv->mesh().stl.facet_start[facet].normal.dot(dir) > 0.);
};
// Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores
// pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
// quickly found by finding a vertex in the list and read the respective facet ids.
std::vector<size_t> facets_to_select{hit_and_facet.second};
NeighborData vertex = std::make_pair(0, 0);
std::vector<bool> visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed
size_t facet_idx = 0; // index into facets_to_select
auto it = neighbors.end();
while (facet_idx < facets_to_select.size()) {
size_t facet = facets_to_select[facet_idx];
if (! visited[facet]) {
// check all three vertices and in case they're close enough, find the remaining facets
// and add them to the list to be proccessed later
for (size_t i=0; i<3; ++i) {
vertex.first = mesh->its.indices[facet](i); // vertex index
float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]);
if (dist < limit) {
it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex);
while (it != neighbors.end() && it->first == vertex.first) {
if (it->second != facet && faces_camera(mv, it->second))
facets_to_select.push_back(it->second);
++it;
}
}
}
visited[facet] = true;
}
++facet_idx;
}
// Now just select all facets that passed.
for (size_t next_facet : facets_to_select) {
SelType& facet = m_selected_facets[mesh_id][next_facet];
if (facet != new_state && facet != SelType::NONE) {
// this triangle is currently in the other VBA.
// Both VBAs need to be refreshed.
update_both = true;
}
facet = new_state;
}
}
update_vertex_buffers(mv, mesh_id,
new_state == SelType::ENFORCER || update_both,
new_state == SelType::BLOCKER || update_both
);
}
if (some_mesh_was_hit)
{
if (m_button_down == Button::None)
m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
// Force rendering. In case the user is dragging, the queue can be
// flooded by wxEVT_MOVING event and rendering would be skipped.
m_parent.render();
return true;
}
if (action == SLAGizmoEventType::Dragging && m_button_down != Button::None) {
// Same as above. We don't want the cursor to freeze when we
// leave the mesh while painting.
m_parent.render();
return true;
}
}
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp)
&& m_button_down != Button::None) {
m_button_down = Button::None;
return true;
}
return false;
}
void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv,
int mesh_id,
bool update_enforcers,
bool update_blockers)
{
const TriangleMesh* mesh = &mv->mesh();
for (SelType type : {SelType::ENFORCER, SelType::BLOCKER}) {
if ((type == SelType::ENFORCER && ! update_enforcers)
|| (type == SelType::BLOCKER && ! update_blockers))
continue;
GLIndexedVertexArray& iva = m_ivas[mesh_id][type==SelType::ENFORCER ? 0 : 1];
iva.release_geometry();
size_t triangle_cnt=0;
for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
SelType status = m_selected_facets[mesh_id][facet_idx];
if (status != type)
continue;
for (int i=0; i<3; ++i)
iva.push_geometry(mesh->its.vertices[mesh->its.indices[facet_idx](i)].cast<double>(),
MeshRaycaster::get_triangle_normal(mesh->its, facet_idx).cast<double>());
iva.push_triangle(3*triangle_cnt, 3*triangle_cnt+1, 3*triangle_cnt+2);
++triangle_cnt;
}
iva.finalize_geometry(true);
}
}
void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit)
{
if (! m_c->selection_info()->model_object())
return;
const float approx_height = m_imgui->scaled(18.0f);
y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
const float minimal_slider_width = m_imgui->scaled(4.f);
float caption_max = 0.f;
float total_text_max = 0.;
for (const std::string& t : {"enforce", "block", "remove"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x);
total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x);
}
caption_max += m_imgui->scaled(1.f);
total_text_max += m_imgui->scaled(1.f);
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
window_width = std::max(window_width, total_text_max);
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
m_imgui->text(caption);
ImGui::PopStyleColor();
ImGui::SameLine(caption_max);
m_imgui->text(text);
};
for (const std::string& t : {"enforce", "block", "remove"})
draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
m_imgui->text("");
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left);
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f)
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(-1., false);
});
}
}
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left);
float clp_dist = m_c->object_clipper()->get_position();
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
m_imgui->end();
}
bool GLGizmoFdmSupports::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF
|| !selection.is_single_full_instance())
return false;
// Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.
const Selection::IndicesList& list = selection.get_volume_idxs();
for (const auto& idx : list)
if (selection.get_volume(idx)->is_outside)
return false;
return true;
}
bool GLGizmoFdmSupports::on_is_selectable() const
{
return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF );
}
std::string GLGizmoFdmSupports::on_get_name() const
{
return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data();
}
CommonGizmosDataID GLGizmoFdmSupports::on_get_requirements() const
{
return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::InstancesHider)
| int(CommonGizmosDataID::Raycaster)
| int(CommonGizmosDataID::HollowedMesh)
| int(CommonGizmosDataID::ObjectClipper)
| int(CommonGizmosDataID::SupportsClipper));
}
void GLGizmoFdmSupports::on_set_state()
{
if (m_state == m_old_state)
return;
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on")));
}
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
// we are actually shutting down
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off")));
m_old_mo = nullptr;
m_ivas.clear();
m_neighbors.clear();
m_selected_facets.clear();
}
m_old_state = m_state;
}
void GLGizmoFdmSupports::on_start_dragging()
{
}
void GLGizmoFdmSupports::on_stop_dragging()
{
}
void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive& ar)
{
}
void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive& ar) const
{
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,100 @@
#ifndef slic3r_GLGizmoFdmSupports_hpp_
#define slic3r_GLGizmoFdmSupports_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include <cereal/types/vector.hpp>
namespace Slic3r {
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
class GLGizmoFdmSupports : public GLGizmoBase
{
private:
const ModelObject* m_old_mo = nullptr;
size_t m_old_volumes_size = 0;
GLUquadricObj* m_quadric;
float m_cursor_radius = 2.f;
static constexpr float CursorRadiusMin = 0.f;
static constexpr float CursorRadiusMax = 8.f;
static constexpr float CursorRadiusStep = 0.2f;
enum class SelType : int8_t {
NONE,
ENFORCER,
BLOCKER
};
// For each model-part volume, store a list of statuses of
// individual facets (one of the enum values above).
std::vector<std::vector<SelType>> m_selected_facets;
// Store two vertex buffer arrays (for enforcers/blockers)
// for each model-part volume.
std::vector<std::array<GLIndexedVertexArray, 2>> m_ivas;
void update_vertex_buffers(const ModelVolume* mv,
int mesh_id,
bool update_enforcers,
bool update_blockers);
public:
GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
~GLGizmoFdmSupports() override;
void set_fdm_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
using NeighborData = std::pair<size_t, size_t>;
private:
bool on_init() override;
void on_render() const override;
void on_render_for_picking() const override;
void render_triangles(const Selection& selection) const;
void render_cursor_circle() const;
void update_mesh();
float m_clipping_plane_distance = 0.f;
std::unique_ptr<ClippingPlane> m_clipping_plane;
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
enum class Button {
None,
Left,
Right
};
Button m_button_down = Button::None;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
std::vector<std::vector<NeighborData>> m_neighbors; // pairs of vertex_index - facet_index for each mesh
protected:
void on_set_state() override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_render_input_window(float x, float y, float bottom_limit) override;
std::string on_get_name() const override;
bool on_is_activable() const override;
bool on_is_selectable() const override;
void on_load(cereal::BinaryInputArchive& ar) override;
void on_save(cereal::BinaryOutputArchive& ar) const override;
CommonGizmosDataID on_get_requirements() const override;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoFdmSupports_hpp_

View File

@ -2,6 +2,7 @@
#include "GLGizmoFlatten.hpp" #include "GLGizmoFlatten.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include <numeric> #include <numeric>
@ -26,20 +27,15 @@ bool GLGizmoFlatten::on_init()
void GLGizmoFlatten::on_set_state() void GLGizmoFlatten::on_set_state()
{ {
// m_model_object pointer can be invalid (for instance because of undo/redo action),
// we should recover it from the object id
m_model_object = nullptr;
for (const auto mo : wxGetApp().model().objects) {
if (mo->id() == m_model_object_id) {
m_model_object = mo;
break;
}
}
if (m_state == On && is_plane_update_necessary()) if (m_state == On && is_plane_update_necessary())
update_planes(); update_planes();
} }
CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const
{
return CommonGizmosDataID::SelectionInfo;
}
std::string GLGizmoFlatten::on_get_name() const std::string GLGizmoFlatten::on_get_name() const
{ {
return (_(L("Place on face")) + " [F]").ToUTF8().data(); return (_(L("Place on face")) + " [F]").ToUTF8().data();
@ -132,18 +128,17 @@ void GLGizmoFlatten::on_render_for_picking() const
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
{ {
m_starting_center = Vec3d::Zero(); m_starting_center = Vec3d::Zero();
if (m_model_object != model_object) { if (model_object != m_old_model_object) {
m_planes.clear(); m_planes.clear();
m_planes_valid = false; m_planes_valid = false;
} }
m_model_object = model_object;
m_model_object_id = model_object ? model_object->id() : 0;
} }
void GLGizmoFlatten::update_planes() void GLGizmoFlatten::update_planes()
{ {
const ModelObject* mo = m_c->selection_info()->model_object();
TriangleMesh ch; TriangleMesh ch;
for (const ModelVolume* vol : m_model_object->volumes) for (const ModelVolume* vol : mo->volumes)
{ {
if (vol->type() != ModelVolumeType::MODEL_PART) if (vol->type() != ModelVolumeType::MODEL_PART)
continue; continue;
@ -153,7 +148,7 @@ void GLGizmoFlatten::update_planes()
} }
ch = ch.convex_hull_3d(); ch = ch.convex_hull_3d();
m_planes.clear(); m_planes.clear();
const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(true); const Transform3d& inst_matrix = mo->instances.front()->get_matrix(true);
// Following constants are used for discarding too small polygons. // Following constants are used for discarding too small polygons.
const float minimal_area = 5.f; // in square mm (world coordinates) const float minimal_area = 5.f; // in square mm (world coordinates)
@ -331,12 +326,13 @@ void GLGizmoFlatten::update_planes()
// Planes are finished - let's save what we calculated it from: // Planes are finished - let's save what we calculated it from:
m_volumes_matrices.clear(); m_volumes_matrices.clear();
m_volumes_types.clear(); m_volumes_types.clear();
for (const ModelVolume* vol : m_model_object->volumes) { for (const ModelVolume* vol : mo->volumes) {
m_volumes_matrices.push_back(vol->get_matrix()); m_volumes_matrices.push_back(vol->get_matrix());
m_volumes_types.push_back(vol->type()); m_volumes_types.push_back(vol->type());
} }
m_first_instance_scale = m_model_object->instances.front()->get_scaling_factor(); m_first_instance_scale = mo->instances.front()->get_scaling_factor();
m_first_instance_mirror = m_model_object->instances.front()->get_mirror(); m_first_instance_mirror = mo->instances.front()->get_mirror();
m_old_model_object = mo;
m_planes_valid = true; m_planes_valid = true;
} }
@ -344,20 +340,22 @@ void GLGizmoFlatten::update_planes()
bool GLGizmoFlatten::is_plane_update_necessary() const bool GLGizmoFlatten::is_plane_update_necessary() const
{ {
if (m_state != On || !m_model_object || m_model_object->instances.empty()) const ModelObject* mo = m_c->selection_info()->model_object();
if (m_state != On || ! mo || mo->instances.empty())
return false; return false;
if (! m_planes_valid || m_model_object->volumes.size() != m_volumes_matrices.size()) if (! m_planes_valid || mo != m_old_model_object
|| mo->volumes.size() != m_volumes_matrices.size())
return true; return true;
// We want to recalculate when the scale changes - some planes could (dis)appear. // We want to recalculate when the scale changes - some planes could (dis)appear.
if (! m_model_object->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale)
|| ! m_model_object->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror))
return true; return true;
for (unsigned int i=0; i < m_model_object->volumes.size(); ++i) for (unsigned int i=0; i < mo->volumes.size(); ++i)
if (! m_model_object->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i])
|| m_model_object->volumes[i]->type() != m_volumes_types[i]) || mo->volumes[i]->type() != m_volumes_types[i])
return true; return true;
return false; return false;

View File

@ -30,8 +30,7 @@ private:
std::vector<PlaneData> m_planes; std::vector<PlaneData> m_planes;
bool m_planes_valid = false; bool m_planes_valid = false;
mutable Vec3d m_starting_center; mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr; const ModelObject* m_old_model_object = nullptr;
ObjectID m_model_object_id = 0;
std::vector<const Transform3d*> instances_matrices; std::vector<const Transform3d*> instances_matrices;
void update_planes(); void update_planes();
@ -51,6 +50,7 @@ protected:
virtual void on_render() const override; virtual void on_render() const override;
virtual void on_render_for_picking() const override; virtual void on_render_for_picking() const override;
virtual void on_set_state() override; virtual void on_set_state() override;
virtual CommonGizmosDataID on_get_requirements() const override;
}; };
} // namespace GUI } // namespace GUI

View File

@ -1,21 +1,15 @@
#include "GLGizmoHollow.hpp" #include "GLGizmoHollow.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/Gizmos/GLGizmos.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include <GL/glew.h> #include <GL/glew.h>
#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Plater.hpp"
#if ENABLE_NON_STATIC_CANVAS_MANAGER
#include "slic3r/GUI/Camera.hpp"
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/PresetBundle.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/TriangleMesh.hpp"
namespace Slic3r { namespace Slic3r {
@ -59,32 +53,14 @@ bool GLGizmoHollow::on_init()
void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
{ {
if (m_c->recent_update) { if (! m_c->selection_info())
return;
if (m_state == On) const ModelObject* mo = m_c->selection_info()->model_object();
m_c->build_AABB_if_needed(); if (mo) {
reload_cache();
update_clipping_plane(m_c->m_clipping_plane_was_moved); if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh())
m_holes_in_drilled_mesh = mo->sla_drain_holes;
// This is a temporary and not very nice hack, to make sure that
// if the cp was moved by the data returned by backend, it will
// remember its direction. FIXME: Refactor this mess and make
// the clipping plane itself part of the shared data.
if (! m_c->m_clipping_plane_was_moved && m_c->m_clipping_plane_distance == 0.25f)
m_c->m_clipping_plane_was_moved = true;
if (m_c->m_model_object) {
reload_cache();
if (m_c->has_drilled_mesh())
m_holes_in_drilled_mesh = m_c->m_model_object->sla_drain_holes;
}
}
if (m_state == On) {
m_parent.toggle_model_objects_visibility(false);
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance);
} }
} }
@ -93,12 +69,12 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
void GLGizmoHollow::on_render() const void GLGizmoHollow::on_render() const
{ {
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info();
// If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off
if (m_state == On if (m_state == On
&& (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] && (sel_info->model_object() != selection.get_model()->objects[selection.get_object_idx()]
|| m_c->m_active_instance != selection.get_instance_idx() || sel_info->get_active_instance() != selection.get_instance_idx())) {
|| m_c->m_model_object_id != m_c->m_model_object->id())) {
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS));
return; return;
} }
@ -106,92 +82,17 @@ void GLGizmoHollow::on_render() const
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
if (m_quadric != nullptr && selection.is_from_single_instance()) if (m_quadric != nullptr && selection.is_from_single_instance())
render_points(selection, false); render_points(selection, false);
m_selection_rectangle.render(m_parent); m_selection_rectangle.render(m_parent);
render_clipping_plane(selection); m_c->object_clipper()->render_cut();
m_c->supports_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
void GLGizmoHollow::render_clipping_plane(const Selection& selection) const
{
if (m_c->m_clipping_plane_distance == 0.f)
return;
// Get transformation of the instance
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = vol->get_instance_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
// Get transformation of supports
Geometry::Transformation supports_trafo;
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z()));
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
// I don't know why, but following seems to be correct.
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
1,
1.));
// Now initialize the TMS for the object, perform the cut and save the result.
if (! m_c->m_object_clipper) {
m_c->m_object_clipper.reset(new MeshClipper);
m_c->m_object_clipper->set_mesh(*m_c->mesh());
}
m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane);
m_c->m_object_clipper->set_transformation(trafo);
if (m_c->m_print_object_idx >= 0) {
const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx];
if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) {
// If the supports are already calculated, save the timestamp of the respective step
// so we can later tell they were recalculated.
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) {
// The timestamp has changed.
m_c->m_supports_clipper.reset(new MeshClipper);
// The mesh should already have the shared vertices calculated.
m_c->m_supports_clipper->set_mesh(print_object->support_mesh());
m_c->m_old_timestamp = timestamp;
}
m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane);
m_c->m_supports_clipper->set_transformation(supports_trafo);
}
else
// The supports are not valid. We better dump the cached data.
m_c->m_supports_clipper.reset();
}
// At this point we have the triangulated cuts for both the object and supports - let's render.
if (! m_c->m_object_clipper->get_triangles().empty()) {
::glPushMatrix();
::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_c->m_object_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
if (m_show_supports && m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty()) {
::glPushMatrix();
::glColor3f(1.0f, 0.f, 0.37f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_c->m_supports_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
}
void GLGizmoHollow::on_render_for_picking() const void GLGizmoHollow::on_render_for_picking() const
{ {
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
@ -213,17 +114,18 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glTranslated(0.0, 0.0, m_z_shift)); glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift()));
glsafe(::glMultMatrixd(instance_matrix.data())); glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[4]; float render_color[4];
size_t cache_size = m_c->m_model_object->sla_drain_holes.size(); const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
size_t cache_size = drain_holes.size();
for (size_t i = 0; i < cache_size; ++i) for (size_t i = 0; i < cache_size; ++i)
{ {
const sla::DrainHole& drain_hole = m_c->m_model_object->sla_drain_holes[i]; const sla::DrainHole& drain_hole = drain_holes[i];
const bool& point_selected = m_selected[i]; const bool& point_selected = m_selected[i];
if (is_mesh_point_clipped((drain_hole.pos+m_c->HoleStickOutLength*drain_hole.normal).cast<double>())) if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast<double>()))
continue; continue;
// First decide about the color of the point. // First decide about the color of the point.
@ -261,7 +163,6 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
glFrontFace(GL_CW); glFrontFace(GL_CW);
// Matrices set, we can render the point mark now. // Matrices set, we can render the point mark now.
Eigen::Quaterniond q; Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast<double>()); q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast<double>());
Eigen::AngleAxisd aa(q); Eigen::AngleAxisd aa(q);
@ -297,12 +198,17 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
{ {
if (m_c->m_clipping_plane_distance == 0.f) if (m_c->object_clipper()->get_position() == 0.)
return false; return false;
Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; auto sel_info = m_c->selection_info();
transformed_point(2) += m_z_shift; int active_inst = m_c->selection_info()->get_active_instance();
return m_c->m_clipping_plane->is_point_clipped(transformed_point); const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
const Transform3d& trafo = mi->get_transformation().get_matrix();
Vec3d transformed_point = trafo * point;
transformed_point(2) += sel_info->get_sla_shift();
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
} }
@ -311,7 +217,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
// Return false if no intersection was found, true otherwise. // Return false if no intersection was found, true otherwise.
bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal) bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
{ {
if (! m_c->m_mesh_raycaster) if (! m_c->raycaster()->raycaster())
return false; return false;
#if ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_NON_STATIC_CANVAS_MANAGER
@ -322,20 +228,23 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = volume->get_instance_transformation(); Geometry::Transformation trafo = volume->get_instance_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
double clp_dist = m_c->object_clipper()->get_position();
const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
// The raycaster query // The raycaster query
Vec3f hit; Vec3f hit;
Vec3f normal; Vec3f normal;
if (m_c->m_mesh_raycaster->unproject_on_mesh( if (m_c->raycaster()->raycaster()->unproject_on_mesh(
mouse_pos, mouse_pos,
trafo.get_matrix(), trafo.get_matrix(),
camera, camera,
hit, hit,
normal, normal,
m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr)) clp_dist != 0. ? clp : nullptr))
{ {
if (m_c->has_drilled_mesh()) { if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) {
// in this case the raycaster sees the hollowed and drilled mesh. // in this case the raycaster sees the hollowed and drilled mesh.
// if the point lies on the surface created by the hole, we want // if the point lies on the surface created by the hole, we want
// to ignore it. // to ignore it.
@ -362,6 +271,10 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
// concludes that the event was not intended for it, it should return false. // concludes that the event was not intended for it, it should return false.
bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{ {
ModelObject* mo = m_c->selection_info()->model_object();
int active_inst = m_c->selection_info()->get_active_instance();
// left down with shift - show the selection rectangle: // left down with shift - show the selection rectangle:
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
if (m_hover_id == -1) { if (m_hover_id == -1) {
@ -393,15 +306,15 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole")));
Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); Vec3d scaling = mo->instances[active_inst]->get_scaling_factor();
Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0), Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0),
pos_and_normal.second(1)/scaling(1), pos_and_normal.second(1)/scaling(1),
pos_and_normal.second(2)/scaling(2)); pos_and_normal.second(2)/scaling(2));
m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + m_c->HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/, mo->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/,
-pos_and_normal.second, m_new_hole_radius, m_new_hole_height); -pos_and_normal.second, m_new_hole_radius, m_new_hole_height);
m_selected.push_back(false); m_selected.push_back(false);
assert(m_selected.size() == m_c->m_model_object->sla_drain_holes.size()); assert(m_selected.size() == mo->sla_drain_holes.size());
m_parent.set_as_dirty(); m_parent.set_as_dirty();
m_wait_for_up_event = true; m_wait_for_up_event = true;
} }
@ -420,11 +333,11 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
// First collect positions of all the points in world coordinates. // First collect positions of all the points in world coordinates.
Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
std::vector<Vec3d> points; std::vector<Vec3d> points;
for (unsigned int i=0; i<m_c->m_model_object->sla_drain_holes.size(); ++i) for (unsigned int i=0; i<mo->sla_drain_holes.size(); ++i)
points.push_back(trafo.get_matrix() * m_c->m_model_object->sla_drain_holes[i].pos.cast<double>()); points.push_back(trafo.get_matrix() * mo->sla_drain_holes[i].pos.cast<double>());
// Now ask the rectangle which of the points are inside. // Now ask the rectangle which of the points are inside.
std::vector<Vec3f> points_inside; std::vector<Vec3f> points_inside;
@ -433,11 +346,9 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
points_inside.push_back(points[idx].cast<float>()); points_inside.push_back(points[idx].cast<float>());
// Only select/deselect points that are actually visible // Only select/deselect points that are actually visible
#if ENABLE_NON_STATIC_CANVAS_MANAGER for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs(
for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->m_clipping_plane.get())) trafo, wxGetApp().plater()->get_camera(), points_inside,
#else m_c->object_clipper()->get_clipping_plane()))
for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get()))
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
{ {
if (rectangle_status == GLSelectionRectangle::Deselect) if (rectangle_status == GLSelectionRectangle::Deselect)
unselect_point(points_idxs[idx]); unselect_point(points_idxs[idx]);
@ -491,20 +402,21 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
} }
if (action == SLAGizmoEventType::MouseWheelUp && control_down) { if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); double pos = m_c->object_clipper()->get_position();
update_clipping_plane(m_c->m_clipping_plane_was_moved); pos = std::min(1., pos + 0.01);
m_c->m_clipping_plane_was_moved = true; m_c->object_clipper()->set_position(pos, true);
return true; return true;
} }
if (action == SLAGizmoEventType::MouseWheelDown && control_down) { if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); double pos = m_c->object_clipper()->get_position();
update_clipping_plane(true); pos = std::max(0., pos - 0.01);
m_c->object_clipper()->set_position(pos, true);
return true; return true;
} }
if (action == SLAGizmoEventType::ResetClippingPlane) { if (action == SLAGizmoEventType::ResetClippingPlane) {
update_clipping_plane(); m_c->object_clipper()->set_position(-1., false);
return true; return true;
} }
@ -514,11 +426,12 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
void GLGizmoHollow::delete_selected_points() void GLGizmoHollow::delete_selected_points()
{ {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole")));
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
for (unsigned int idx=0; idx<m_c->m_model_object->sla_drain_holes.size(); ++idx) { for (unsigned int idx=0; idx<drain_holes.size(); ++idx) {
if (m_selected[idx]) { if (m_selected[idx]) {
m_selected.erase(m_selected.begin()+idx); m_selected.erase(m_selected.begin()+idx);
m_c->m_model_object->sla_drain_holes.erase(m_c->m_model_object->sla_drain_holes.begin() + (idx--)); drain_holes.erase(drain_holes.begin() + (idx--));
} }
} }
@ -527,12 +440,14 @@ void GLGizmoHollow::delete_selected_points()
void GLGizmoHollow::on_update(const UpdateData& data) void GLGizmoHollow::on_update(const UpdateData& data)
{ {
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
if (m_hover_id != -1) { if (m_hover_id != -1) {
std::pair<Vec3f, Vec3f> pos_and_normal; std::pair<Vec3f, Vec3f> pos_and_normal;
if (! unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal)) if (! unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal))
return; return;
m_c->m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + m_c->HoleStickOutLength * pos_and_normal.second; drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second;
m_c->m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; drain_holes[m_hover_id].normal = -pos_and_normal.second;
} }
} }
@ -540,19 +455,22 @@ void GLGizmoHollow::on_update(const UpdateData& data)
void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) void GLGizmoHollow::hollow_mesh(bool postpone_error_messages)
{ {
wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().CallAfter([this, postpone_error_messages]() {
wxGetApp().plater()->reslice_SLA_hollowing(*m_c->m_model_object, postpone_error_messages); wxGetApp().plater()->reslice_SLA_hollowing(
*m_c->selection_info()->model_object(), postpone_error_messages);
}); });
} }
std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>>
GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const
{ {
std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> out; std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> out;
const ModelObject* mo = m_c->selection_info()->model_object();
if (!m_c->m_model_object) if (! mo)
return out; return out;
const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; const DynamicPrintConfig& object_cfg = mo->config;
const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr; std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
@ -573,18 +491,10 @@ std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> GLGizmoHollo
} }
ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const
{
if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f)
return ClippingPlane::ClipsNothing();
else
return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]);
}
void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit)
{ {
if (! m_c->m_model_object) ModelObject* mo = m_c->selection_info()->model_object();
if (! mo)
return; return;
bool first_run = true; // This is a hack to redraw the button when all points are removed, bool first_run = true; // This is a hack to redraw the button when all points are removed,
@ -650,7 +560,7 @@ RENDER_AGAIN:
auto opts = get_config_options({"hollowing_enable"}); auto opts = get_config_options({"hollowing_enable"});
m_enable_hollowing = static_cast<const ConfigOptionBool*>(opts[0].first)->value; m_enable_hollowing = static_cast<const ConfigOptionBool*>(opts[0].first)->value;
if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) {
m_c->m_model_object->config.opt<ConfigOptionBool>("hollowing_enable", true)->value = m_enable_hollowing; mo->config.opt<ConfigOptionBool>("hollowing_enable", true)->value = m_enable_hollowing;
wxGetApp().obj_list()->update_and_show_object_settings_item(); wxGetApp().obj_list()->update_and_show_object_settings_item();
config_changed = true; config_changed = true;
} }
@ -712,14 +622,14 @@ RENDER_AGAIN:
} }
if (slider_edited || slider_released) { if (slider_edited || slider_released) {
if (slider_released) { if (slider_released) {
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = m_offset_stash; mo->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = m_offset_stash;
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = m_quality_stash; mo->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = m_quality_stash;
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = m_closing_d_stash; mo->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = m_closing_d_stash;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change")));
} }
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = offset; mo->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = offset;
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = quality; mo->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = quality;
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = closing_d; mo->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = closing_d;
if (slider_released) { if (slider_released) {
wxGetApp().obj_list()->update_and_show_object_settings_item(); wxGetApp().obj_list()->update_and_show_object_settings_item();
config_changed = true; config_changed = true;
@ -750,9 +660,9 @@ RENDER_AGAIN:
m_imgui->text(m_desc["hole_depth"]); m_imgui->text(m_desc["hole_depth"]);
ImGui::SameLine(diameter_slider_left); ImGui::SameLine(diameter_slider_left);
m_new_hole_height -= m_c->HoleStickOutLength; m_new_hole_height -= HoleStickOutLength;
ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm"); ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm");
m_new_hole_height += m_c->HoleStickOutLength; m_new_hole_height += HoleStickOutLength;
clicked |= ImGui::IsItemClicked(); clicked |= ImGui::IsItemClicked();
edited |= ImGui::IsItemEdited(); edited |= ImGui::IsItemEdited();
@ -764,19 +674,19 @@ RENDER_AGAIN:
// - take correct undo/redo snapshot after the user is done with moving the slider // - take correct undo/redo snapshot after the user is done with moving the slider
if (! m_selection_empty) { if (! m_selection_empty) {
if (clicked) { if (clicked) {
m_holes_stash = m_c->m_model_object->sla_drain_holes; m_holes_stash = mo->sla_drain_holes;
} }
if (edited) { if (edited) {
for (size_t idx=0; idx<m_selected.size(); ++idx) for (size_t idx=0; idx<m_selected.size(); ++idx)
if (m_selected[idx]) { if (m_selected[idx]) {
m_c->m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; mo->sla_drain_holes[idx].radius = m_new_hole_radius;
m_c->m_model_object->sla_drain_holes[idx].height = m_new_hole_height; mo->sla_drain_holes[idx].height = m_new_hole_height;
} }
} }
if (deactivated) { if (deactivated) {
// momentarily restore the old value to take snapshot // momentarily restore the old value to take snapshot
sla::DrainHoles new_holes = m_c->m_model_object->sla_drain_holes; sla::DrainHoles new_holes = mo->sla_drain_holes;
m_c->m_model_object->sla_drain_holes = m_holes_stash; mo->sla_drain_holes = m_holes_stash;
float backup_rad = m_new_hole_radius; float backup_rad = m_new_hole_radius;
float backup_hei = m_new_hole_height; float backup_hei = m_new_hole_height;
for (size_t i=0; i<m_holes_stash.size(); ++i) { for (size_t i=0; i<m_holes_stash.size(); ++i) {
@ -789,7 +699,7 @@ RENDER_AGAIN:
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter")));
m_new_hole_radius = backup_rad; m_new_hole_radius = backup_rad;
m_new_hole_height = backup_hei; m_new_hole_height = backup_hei;
m_c->m_model_object->sla_drain_holes = new_holes; mo->sla_drain_holes = new_holes;
} }
} }
@ -797,33 +707,33 @@ RENDER_AGAIN:
remove_selected = m_imgui->button(m_desc.at("remove_selected")); remove_selected = m_imgui->button(m_desc.at("remove_selected"));
m_imgui->disabled_end(); m_imgui->disabled_end();
m_imgui->disabled_begin(m_c->m_model_object->sla_drain_holes.empty()); m_imgui->disabled_begin(mo->sla_drain_holes.empty());
remove_all = m_imgui->button(m_desc.at("remove_all")); remove_all = m_imgui->button(m_desc.at("remove_all"));
m_imgui->disabled_end(); m_imgui->disabled_end();
// Following is rendered in both editing and non-editing mode: // Following is rendered in both editing and non-editing mode:
// m_imgui->text(""); // m_imgui->text("");
ImGui::Separator(); ImGui::Separator();
if (m_c->m_clipping_plane_distance == 0.f) if (m_c->object_clipper()->get_position() == 0.f)
m_imgui->text(m_desc.at("clipping_of_view")); m_imgui->text(m_desc.at("clipping_of_view"));
else { else {
if (m_imgui->button(m_desc.at("reset_direction"))) { if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){ wxGetApp().CallAfter([this](){
update_clipping_plane(); m_c->object_clipper()->set_position(-1., false);
}); });
} }
} }
ImGui::SameLine(clipping_slider_left); ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left);
if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) { float clp_dist = m_c->object_clipper()->get_position();
update_clipping_plane(m_c->m_clipping_plane_was_moved); if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->m_clipping_plane_was_moved = true; m_c->object_clipper()->set_position(clp_dist, true);
}
// make sure supports are shown/hidden as appropriate // make sure supports are shown/hidden as appropriate
if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { bool show_sups = m_c->instances_hider()->are_supports_shown();
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) {
m_c->instances_hider()->show_supports(show_sups);
force_refresh = true; force_refresh = true;
} }
@ -882,54 +792,30 @@ std::string GLGizmoHollow::on_get_name() const
} }
CommonGizmosDataID GLGizmoHollow::on_get_requirements() const
{
return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::InstancesHider)
| int(CommonGizmosDataID::Raycaster)
| int(CommonGizmosDataID::HollowedMesh)
| int(CommonGizmosDataID::ObjectClipper)
| int(CommonGizmosDataID::SupportsClipper));
}
void GLGizmoHollow::on_set_state() void GLGizmoHollow::on_set_state()
{ {
// m_c->m_model_object pointer can be invalid (for instance because of undo/redo action),
// we should recover it from the object id
m_c->m_model_object = nullptr;
for (const auto mo : wxGetApp().model().objects) {
if (mo->id() == m_c->m_model_object_id) {
m_c->m_model_object = mo;
break;
}
}
if (m_state == m_old_state) if (m_state == m_old_state)
return; return;
if (m_state == On && m_old_state != On) { // the gizmo was just turned on if (m_state == On && m_old_state != On) { // the gizmo was just turned on
//Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
//m_c->update_from_backend(m_parent, m_c->m_model_object);
m_c->unstash_clipping_plane();
update_clipping_plane(m_c->m_clipping_plane_was_moved);
m_c->build_AABB_if_needed();
// we'll now reload support points: // we'll now reload support points:
if (m_c->m_model_object) if (m_c->selection_info()->model_object())
reload_cache(); reload_cache();
m_parent.toggle_model_objects_visibility(false);
if (m_c->m_model_object) {
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance);
}
} }
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off
//Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
m_parent.toggle_model_objects_visibility(true);
m_c->stash_clipping_plane();
m_c->m_clipping_plane_distance = 0.f;
update_clipping_plane(true);
// Release clippers and the AABB raycaster.
m_c->m_object_clipper.reset();
m_c->m_supports_clipper.reset();
//m_c->m_mesh_raycaster.reset();
//m_c->m_cavity_mesh.reset();
//m_c->m_volume_with_cavity.reset();
}
m_old_state = m_state; m_old_state = m_state;
} }
@ -940,7 +826,7 @@ void GLGizmoHollow::on_start_dragging()
if (m_hover_id != -1) { if (m_hover_id != -1) {
select_point(NoPoints); select_point(NoPoints);
select_point(m_hover_id); select_point(m_hover_id);
m_hole_before_drag = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; m_hole_before_drag = m_c->selection_info()->model_object()->sla_drain_holes[m_hover_id].pos;
} }
else else
m_hole_before_drag = Vec3f::Zero(); m_hole_before_drag = Vec3f::Zero();
@ -949,15 +835,16 @@ void GLGizmoHollow::on_start_dragging()
void GLGizmoHollow::on_stop_dragging() void GLGizmoHollow::on_stop_dragging()
{ {
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
if (m_hover_id != -1) { if (m_hover_id != -1) {
Vec3f backup = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; Vec3f backup = drain_holes[m_hover_id].pos;
if (m_hole_before_drag != Vec3f::Zero() // some point was touched if (m_hole_before_drag != Vec3f::Zero() // some point was touched
&& backup != m_hole_before_drag) // and it was moved, not just selected && backup != m_hole_before_drag) // and it was moved, not just selected
{ {
m_c->m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; drain_holes[m_hover_id].pos = m_hole_before_drag;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole")));
m_c->m_model_object->sla_drain_holes[m_hover_id].pos = backup; drain_holes[m_hover_id].pos = backup;
} }
} }
m_hole_before_drag = Vec3f::Zero(); m_hole_before_drag = Vec3f::Zero();
@ -967,10 +854,7 @@ void GLGizmoHollow::on_stop_dragging()
void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar)
{ {
ar(m_c->m_clipping_plane_distance, ar(m_new_hole_radius,
*m_c->m_clipping_plane,
m_c->m_model_object_id,
m_new_hole_radius,
m_new_hole_height, m_new_hole_height,
m_selected, m_selected,
m_selection_empty m_selection_empty
@ -981,10 +865,7 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar)
void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const
{ {
ar(m_c->m_clipping_plane_distance, ar(m_new_hole_radius,
*m_c->m_clipping_plane,
m_c->m_model_object_id,
m_new_hole_radius,
m_new_hole_height, m_new_hole_height,
m_selected, m_selected,
m_selection_empty m_selection_empty
@ -995,13 +876,15 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const
void GLGizmoHollow::select_point(int i) void GLGizmoHollow::select_point(int i)
{ {
const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
if (i == AllPoints || i == NoPoints) { if (i == AllPoints || i == NoPoints) {
m_selected.assign(m_selected.size(), i == AllPoints); m_selected.assign(m_selected.size(), i == AllPoints);
m_selection_empty = (i == NoPoints); m_selection_empty = (i == NoPoints);
if (i == AllPoints) { if (i == AllPoints) {
m_new_hole_radius = m_c->m_model_object->sla_drain_holes[0].radius; m_new_hole_radius = drain_holes[0].radius;
m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height; m_new_hole_height = drain_holes[0].height;
} }
} }
else { else {
@ -1009,8 +892,8 @@ void GLGizmoHollow::select_point(int i)
m_selected.push_back(false); m_selected.push_back(false);
m_selected[i] = true; m_selected[i] = true;
m_selection_empty = false; m_selection_empty = false;
m_new_hole_radius = m_c->m_model_object->sla_drain_holes[i].radius; m_new_hole_radius = drain_holes[i].radius;
m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height; m_new_hole_height = drain_holes[i].height;
} }
} }
@ -1030,31 +913,13 @@ void GLGizmoHollow::unselect_point(int i)
void GLGizmoHollow::reload_cache() void GLGizmoHollow::reload_cache()
{ {
m_selected.clear(); m_selected.clear();
m_selected.assign(m_c->m_model_object->sla_drain_holes.size(), false); m_selected.assign(m_c->selection_info()->model_object()->sla_drain_holes.size(), false);
}
void GLGizmoHollow::update_clipping_plane(bool keep_normal) const
{
if (! m_c->m_model_object)
return;
#if ENABLE_NON_STATIC_CANVAS_MANAGER
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
m_c->m_clipping_plane->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward());
#else
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward());
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
float dist = normal.dot(center);
*m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius));
m_parent.set_as_dirty();
} }
void GLGizmoHollow::on_set_hover_id() void GLGizmoHollow::on_set_hover_id()
{ {
if (int(m_c->m_model_object->sla_drain_holes.size()) <= m_hover_id) if (int(m_c->selection_info()->model_object()->sla_drain_holes.size()) <= m_hover_id)
m_hover_id = -1; m_hover_id = -1;
} }

View File

@ -13,16 +13,11 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
class ClippingPlane;
class MeshClipper;
class MeshRaycaster;
class CommonGizmosData;
enum class SLAGizmoEventType : unsigned char; enum class SLAGizmoEventType : unsigned char;
class GLGizmoHollow : public GLGizmoBase class GLGizmoHollow : public GLGizmoBase
{ {
private: private:
mutable double m_z_shift = 0.;
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal); bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
GLUquadricObj* m_quadric; GLUquadricObj* m_quadric;
@ -33,12 +28,10 @@ public:
~GLGizmoHollow() override; ~GLGizmoHollow() override;
void set_sla_support_data(ModelObject* model_object, const Selection& selection); void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points(); void delete_selected_points();
ClippingPlane get_sla_clipping_plane() const; bool is_selection_rectangle_dragging() const {
return m_selection_rectangle.is_dragging();
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } }
void update_clipping_plane(bool keep_normal = false) const;
void set_common_data_ptr(CommonGizmosData* ptr) { m_c = ptr; }
private: private:
bool on_init() override; bool on_init() override;
@ -47,11 +40,10 @@ private:
void on_render_for_picking() const override; void on_render_for_picking() const override;
void render_points(const Selection& selection, bool picking = false) const; void render_points(const Selection& selection, bool picking = false) const;
void render_clipping_plane(const Selection& selection) const;
void hollow_mesh(bool postpone_error_messages = false); void hollow_mesh(bool postpone_error_messages = false);
bool unsaved_changes() const; bool unsaved_changes() const;
bool m_show_supports = true; // bool m_show_supports = true;
float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_radius = 2.f; // Size of a new hole.
float m_new_hole_height = 6.f; float m_new_hole_height = 6.f;
mutable std::vector<bool> m_selected; // which holes are currently selected mutable std::vector<bool> m_selected; // which holes are currently selected
@ -67,10 +59,6 @@ private:
sla::DrainHoles m_holes_in_drilled_mesh; sla::DrainHoles m_holes_in_drilled_mesh;
sla::DrainHoles m_holes_stash; sla::DrainHoles m_holes_stash;
CommonGizmosData* m_c = nullptr;
//std::unique_ptr<ClippingPlane> m_clipping_plane;
// This map holds all translated description texts, so they can be easily referenced during layout calculations // This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
@ -101,6 +89,7 @@ protected:
void on_start_dragging() override; void on_start_dragging() override;
void on_stop_dragging() override; void on_stop_dragging() override;
void on_render_input_window(float x, float y, float bottom_limit) override; void on_render_input_window(float x, float y, float bottom_limit) override;
virtual CommonGizmosDataID on_get_requirements() const override;
std::string on_get_name() const override; std::string on_get_name() const override;
bool on_is_activable() const override; bool on_is_activable() const override;

View File

@ -2,7 +2,7 @@
#include "GLGizmoSlaSupports.hpp" #include "GLGizmoSlaSupports.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/Gizmos/GLGizmos.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include <GL/glew.h> #include <GL/glew.h>
@ -14,10 +14,6 @@
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp"
#if ENABLE_NON_STATIC_CANVAS_MANAGER
#include "slic3r/GUI/Camera.hpp"
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
#include "slic3r/GUI/MeshUtils.hpp"
#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/PresetBundle.hpp"
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/SLAPrint.hpp"
@ -29,7 +25,6 @@ namespace GUI {
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id)
, m_quadric(nullptr) , m_quadric(nullptr)
, m_its(nullptr)
{ {
m_quadric = ::gluNewQuadric(); m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr) if (m_quadric != nullptr)
@ -66,26 +61,21 @@ bool GLGizmoSlaSupports::on_init()
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection)
{ {
if (m_c->recent_update) { if (! m_c->selection_info())
if (m_state == On) return;
m_c->build_AABB_if_needed();
update_clipping_plane(m_c->m_clipping_plane_was_moved); ModelObject* mo = m_c->selection_info()->model_object();
if (mo != m_old_mo) {
disable_editing_mode(); disable_editing_mode();
if (m_c->m_model_object) if (mo)
reload_cache(); reload_cache();
} m_old_mo = mo;
if (m_state == On) {
m_parent.toggle_model_objects_visibility(false);
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance);
} }
// If we triggered autogeneration before, check backend and fetch results if they are there // If we triggered autogeneration before, check backend and fetch results if they are there
if (m_c->m_model_object) { if (mo) {
if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) if (mo->sla_points_status == sla::PointsStatus::Generating)
get_data_from_backend(); get_data_from_backend();
} }
} }
@ -94,13 +84,13 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
void GLGizmoSlaSupports::on_render() const void GLGizmoSlaSupports::on_render() const
{ {
ModelObject* mo = m_c->selection_info()->model_object();
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
// If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off
if (m_state == On if (m_state == On
&& (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] && (mo != selection.get_model()->objects[selection.get_object_idx()]
|| m_c->m_active_instance != selection.get_instance_idx() || m_c->selection_info()->get_active_instance() != selection.get_instance_idx())) {
|| m_c->m_model_object_id != m_c->m_model_object->id())) {
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS));
return; return;
} }
@ -108,113 +98,20 @@ void GLGizmoSlaSupports::on_render() const
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
if (m_quadric != nullptr && selection.is_from_single_instance()) if (m_quadric != nullptr && selection.is_from_single_instance())
render_points(selection, false); render_points(selection, false);
m_selection_rectangle.render(m_parent); m_selection_rectangle.render(m_parent);
render_clipping_plane(selection); m_c->object_clipper()->render_cut();
m_c->supports_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
{
if (m_c->m_clipping_plane_distance == 0.f || m_c->m_mesh->empty())
return;
// Get transformation of the instance
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = vol->get_instance_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
// Get transformation of supports
Geometry::Transformation supports_trafo;
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z()));
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
// I don't know why, but following seems to be correct.
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
1,
1.));
// Now initialize the TMS for the object, perform the cut and save the result.
if (! m_c->m_object_clipper) {
m_c->m_object_clipper.reset(new MeshClipper);
m_c->m_object_clipper->set_mesh(*m_c->mesh());
}
m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane);
m_c->m_object_clipper->set_transformation(trafo);
// Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
// cached so we don't have todo it on each render. We only search for the po if needed:
if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) {
m_c->m_print_objects_count = m_parent.sla_print()->objects().size();
m_c->m_print_object_idx = -1;
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
++m_c->m_print_object_idx;
if (po->model_object()->id() == m_c->m_model_object->id())
break;
}
}
if (m_c->m_print_object_idx >= 0) {
const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx];
if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) {
// If the supports are already calculated, save the timestamp of the respective step
// so we can later tell they were recalculated.
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) {
// The timestamp has changed.
m_c->m_supports_clipper.reset(new MeshClipper);
// The mesh should already have the shared vertices calculated.
m_c->m_supports_clipper->set_mesh(print_object->support_mesh());
m_c->m_old_timestamp = timestamp;
}
m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane);
m_c->m_supports_clipper->set_transformation(supports_trafo);
}
else
// The supports are not valid. We better dump the cached data.
m_c->m_supports_clipper.reset();
}
// At this point we have the triangulated cuts for both the object and supports - let's render.
if (! m_c->m_object_clipper->get_triangles().empty()) {
::glPushMatrix();
::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_c->m_object_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
if (m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty() && !m_editing_mode) {
// The supports are hidden in the editing mode, so it makes no sense to render the cuts.
::glPushMatrix();
::glColor3f(1.0f, 0.f, 0.37f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_c->m_supports_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
}
void GLGizmoSlaSupports::on_render_for_picking() const void GLGizmoSlaSupports::on_render_for_picking() const
{ {
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
#if ENABLE_RENDER_PICKING_PASS
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
#endif
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true); render_points(selection, true);
} }
@ -227,9 +124,10 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
float z_shift = m_c->selection_info()->get_sla_shift();
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glTranslated(0.0, 0.0, m_z_shift)); glsafe(::glTranslated(0.0, 0.0, z_shift));
glsafe(::glMultMatrixd(instance_matrix.data())); glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[4]; float render_color[4];
@ -285,7 +183,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
if (m_editing_mode) { if (m_editing_mode) {
// in case the normal is not yet cached, find and cache it // in case the normal is not yet cached, find and cache it
if (m_editing_cache[i].normal == Vec3f::Zero()) if (m_editing_cache[i].normal == Vec3f::Zero())
m_c->m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
Eigen::Quaterniond q; Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>()); q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
@ -315,14 +213,15 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
} }
// Now render the drain holes: // Now render the drain holes:
if (! m_c->has_drilled_mesh()) { //if (! m_c->has_drilled_mesh()) {
if (! m_c->hollowed_mesh()->get_hollowed_mesh()) {
render_color[0] = 0.7f; render_color[0] = 0.7f;
render_color[1] = 0.7f; render_color[1] = 0.7f;
render_color[2] = 0.7f; render_color[2] = 0.7f;
render_color[3] = 0.7f; render_color[3] = 0.7f;
glsafe(::glColor4fv(render_color)); glsafe(::glColor4fv(render_color));
for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
if (is_mesh_point_clipped((drain_hole.pos+m_c->HoleStickOutLength*drain_hole.normal).cast<double>())) if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast<double>()))
continue; continue;
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
@ -365,12 +264,17 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
{ {
if (m_c->m_clipping_plane_distance == 0.f) if (m_c->object_clipper()->get_position() == 0.)
return false; return false;
Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; auto sel_info = m_c->selection_info();
transformed_point(2) += m_z_shift; int active_inst = m_c->selection_info()->get_active_instance();
return m_c->m_clipping_plane->is_point_clipped(transformed_point); const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
const Transform3d& trafo = mi->get_transformation().get_matrix();
Vec3d transformed_point = trafo * point;
transformed_point(2) += sel_info->get_sla_shift();
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
} }
@ -379,37 +283,37 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
// Return false if no intersection was found, true otherwise. // Return false if no intersection was found, true otherwise.
bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal) bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
{ {
if (! m_c->m_mesh_raycaster) if (! m_c->raycaster()->raycaster())
return false; return false;
#if ENABLE_NON_STATIC_CANVAS_MANAGER
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
#else
const Camera& camera = m_parent.get_camera();
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = volume->get_instance_transformation(); Geometry::Transformation trafo = volume->get_instance_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
double clp_dist = m_c->object_clipper()->get_position();
const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
// The raycaster query // The raycaster query
Vec3f hit; Vec3f hit;
Vec3f normal; Vec3f normal;
if (m_c->m_mesh_raycaster->unproject_on_mesh( if (m_c->raycaster()->raycaster()->unproject_on_mesh(
mouse_pos, mouse_pos,
trafo.get_matrix(), trafo.get_matrix(),
camera, camera,
hit, hit,
normal, normal,
m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr)) clp_dist != 0. ? clp : nullptr))
{ {
// Check whether the hit is in a hole // Check whether the hit is in a hole
bool in_hole = false; bool in_hole = false;
// In case the hollowed and drilled mesh is available, we can allow // In case the hollowed and drilled mesh is available, we can allow
// placing points in holes, because they should never end up // placing points in holes, because they should never end up
// on surface that's been drilled away. // on surface that's been drilled away.
if (! m_c->has_drilled_mesh()) { if (! m_c->hollowed_mesh()->get_hollowed_mesh()) {
for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
for (const sla::DrainHole& hole : drain_holes) {
if (hole.is_inside(hit)) { if (hole.is_inside(hit)) {
in_hole = true; in_hole = true;
break; break;
@ -432,6 +336,9 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
// concludes that the event was not intended for it, it should return false. // concludes that the event was not intended for it, it should return false.
bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{ {
ModelObject* mo = m_c->selection_info()->model_object();
int active_inst = m_c->selection_info()->get_active_instance();
if (m_editing_mode) { if (m_editing_mode) {
// left down with shift - show the selection rectangle: // left down with shift - show the selection rectangle:
@ -483,8 +390,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
// First collect positions of all the points in world coordinates. // First collect positions of all the points in world coordinates.
Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
std::vector<Vec3d> points; std::vector<Vec3d> points;
for (unsigned int i=0; i<m_editing_cache.size(); ++i) for (unsigned int i=0; i<m_editing_cache.size(); ++i)
points.push_back(trafo.get_matrix() * m_editing_cache[i].support_point.pos.cast<double>()); points.push_back(trafo.get_matrix() * m_editing_cache[i].support_point.pos.cast<double>());
@ -496,11 +403,9 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
points_inside.push_back(points[idx].cast<float>()); points_inside.push_back(points[idx].cast<float>());
// Only select/deselect points that are actually visible // Only select/deselect points that are actually visible
#if ENABLE_NON_STATIC_CANVAS_MANAGER for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs(
for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->m_clipping_plane.get())) trafo, wxGetApp().plater()->get_camera(), points_inside,
#else m_c->object_clipper()->get_clipping_plane()))
for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get()))
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
{ {
if (rectangle_status == GLSelectionRectangle::Deselect) if (rectangle_status == GLSelectionRectangle::Deselect)
unselect_point(points_idxs[idx]); unselect_point(points_idxs[idx]);
@ -577,20 +482,21 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
} }
if (action == SLAGizmoEventType::MouseWheelUp && control_down) { if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); double pos = m_c->object_clipper()->get_position();
update_clipping_plane(m_c->m_clipping_plane_was_moved); pos = std::min(1., pos + 0.01);
m_c->m_clipping_plane_was_moved = true; m_c->object_clipper()->set_position(pos, true);
return true; return true;
} }
if (action == SLAGizmoEventType::MouseWheelDown && control_down) { if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); double pos = m_c->object_clipper()->get_position();
update_clipping_plane(true); pos = std::max(0., pos - 0.01);
m_c->object_clipper()->set_position(pos, true);
return true; return true;
} }
if (action == SLAGizmoEventType::ResetClippingPlane) { if (action == SLAGizmoEventType::ResetClippingPlane) {
update_clipping_plane(); m_c->object_clipper()->set_position(-1., false);
return true; return true;
} }
@ -634,11 +540,12 @@ void GLGizmoSlaSupports::on_update(const UpdateData& data)
std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
{ {
std::vector<const ConfigOption*> out; std::vector<const ConfigOption*> out;
const ModelObject* mo = m_c->selection_info()->model_object();
if (!m_c->m_model_object) if (! mo)
return out; return out;
const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; const DynamicPrintConfig& object_cfg = mo->config;
const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr; std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
@ -659,14 +566,6 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st
} }
ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
{
if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f)
return ClippingPlane::ClipsNothing();
else
return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]);
}
/* /*
void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& idxs) const void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& idxs) const
@ -714,7 +613,9 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l
static float last_y = 0.0f; static float last_y = 0.0f;
static float last_h = 0.0f; static float last_h = 0.0f;
if (! m_c->m_model_object) ModelObject* mo = m_c->selection_info()->model_object();
if (! mo)
return; return;
bool first_run = true; // This is a hack to redraw the button when all points are removed, bool first_run = true; // This is a hack to redraw the button when all points are removed,
@ -851,15 +752,15 @@ RENDER_AGAIN:
m_density_stash = density; m_density_stash = density;
} }
if (slider_edited) { if (slider_edited) {
m_c->m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance; mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_c->m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density; mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
} }
if (slider_released) { if (slider_released) {
m_c->m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash;
m_c->m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash; mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change")));
m_c->m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance; mo->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_c->m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density; mo->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
wxGetApp().obj_list()->update_and_show_object_settings_item(); wxGetApp().obj_list()->update_and_show_object_settings_item();
} }
@ -886,7 +787,7 @@ RENDER_AGAIN:
// Following is rendered in both editing and non-editing mode: // Following is rendered in both editing and non-editing mode:
ImGui::Separator(); ImGui::Separator();
if (m_c->m_clipping_plane_distance == 0.f) if (m_c->object_clipper()->get_position() == 0.f)
{ {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view")); m_imgui->text(m_desc.at("clipping_of_view"));
@ -894,17 +795,16 @@ RENDER_AGAIN:
else { else {
if (m_imgui->button(m_desc.at("reset_direction"))) { if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){ wxGetApp().CallAfter([this](){
update_clipping_plane(); m_c->object_clipper()->set_position(-1., false);
}); });
} }
} }
ImGui::SameLine(clipping_slider_left); ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left);
if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) { float clp_dist = m_c->object_clipper()->get_position();
update_clipping_plane(m_c->m_clipping_plane_was_moved); if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->m_clipping_plane_was_moved = true; m_c->object_clipper()->set_position(clp_dist, true);
}
if (m_imgui->button("?")) { if (m_imgui->button("?")) {
@ -969,18 +869,22 @@ std::string GLGizmoSlaSupports::on_get_name() const
} }
CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
{
return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::InstancesHider)
| int(CommonGizmosDataID::Raycaster)
| int(CommonGizmosDataID::HollowedMesh)
| int(CommonGizmosDataID::ObjectClipper)
| int(CommonGizmosDataID::SupportsClipper));
}
void GLGizmoSlaSupports::on_set_state() void GLGizmoSlaSupports::on_set_state()
{ {
// m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), const ModelObject* mo = m_c->selection_info()->model_object();
// we should recover it from the object id
m_c->m_model_object = nullptr;
for (const auto mo : wxGetApp().model().objects) {
if (mo->id() == m_c->m_model_object_id) {
m_c->m_model_object = mo;
break;
}
}
if (m_state == m_old_state) if (m_state == m_old_state)
return; return;
@ -988,28 +892,16 @@ void GLGizmoSlaSupports::on_set_state()
if (m_state == On && m_old_state != On) { // the gizmo was just turned on if (m_state == On && m_old_state != On) { // the gizmo was just turned on
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
m_c->unstash_clipping_plane();
update_clipping_plane(m_c->m_clipping_plane_was_moved);
m_c->build_AABB_if_needed();
// we'll now reload support points: // we'll now reload support points:
if (m_c->m_model_object) if (mo)
reload_cache(); reload_cache();
m_parent.toggle_model_objects_visibility(false);
if (m_c->m_model_object) {
m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance);
m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance);
}
// Set default head diameter from config. // Set default head diameter from config.
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value; m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
} }
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
bool will_ask = m_c->m_model_object && m_editing_mode && unsaved_changes(); bool will_ask = mo && m_editing_mode && unsaved_changes();
if (will_ask) { if (will_ask) {
wxGetApp().CallAfter([this]() { wxGetApp().CallAfter([this]() {
// Following is called through CallAfter, because otherwise there was a problem // Following is called through CallAfter, because otherwise there was a problem
@ -1028,16 +920,8 @@ void GLGizmoSlaSupports::on_set_state()
// we are actually shutting down // we are actually shutting down
disable_editing_mode(); // so it is not active next time the gizmo opens disable_editing_mode(); // so it is not active next time the gizmo opens
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
m_parent.toggle_model_objects_visibility(true);
m_normal_cache.clear(); m_normal_cache.clear();
m_c->stash_clipping_plane();
m_c->m_clipping_plane_distance = 0.f;
update_clipping_plane(true);
// Release clippers and the AABB raycaster.
m_its = nullptr;
m_c->m_object_clipper.reset();
m_c->m_supports_clipper.reset();
//m_c->m_mesh_raycaster.reset();
} }
} }
m_old_state = m_state; m_old_state = m_state;
@ -1077,10 +961,7 @@ void GLGizmoSlaSupports::on_stop_dragging()
void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
{ {
ar(m_c->m_clipping_plane_distance, ar(m_new_point_head_diameter,
*m_c->m_clipping_plane,
m_c->m_model_object_id,
m_new_point_head_diameter,
m_normal_cache, m_normal_cache,
m_editing_cache, m_editing_cache,
m_selection_empty m_selection_empty
@ -1091,10 +972,7 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const
{ {
ar(m_c->m_clipping_plane_distance, ar(m_new_point_head_diameter,
*m_c->m_clipping_plane,
m_c->m_model_object_id,
m_new_point_head_diameter,
m_normal_cache, m_normal_cache,
m_editing_cache, m_editing_cache,
m_selection_empty m_selection_empty
@ -1171,9 +1049,10 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
for (const CacheEntry& ce : m_editing_cache) for (const CacheEntry& ce : m_editing_cache)
m_normal_cache.push_back(ce.support_point); m_normal_cache.push_back(ce.support_point);
m_c->m_model_object->sla_points_status = sla::PointsStatus::UserModified; ModelObject* mo = m_c->selection_info()->model_object();
m_c->m_model_object->sla_support_points.clear(); mo->sla_points_status = sla::PointsStatus::UserModified;
m_c->m_model_object->sla_support_points = m_normal_cache; mo->sla_support_points.clear();
mo->sla_support_points = m_normal_cache;
reslice_SLA_supports(); reslice_SLA_supports();
} }
@ -1183,23 +1062,25 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
void GLGizmoSlaSupports::reload_cache() void GLGizmoSlaSupports::reload_cache()
{ {
const ModelObject* mo = m_c->selection_info()->model_object();
m_normal_cache.clear(); m_normal_cache.clear();
if (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) if (mo->sla_points_status == sla::PointsStatus::AutoGenerated || mo->sla_points_status == sla::PointsStatus::Generating)
get_data_from_backend(); get_data_from_backend();
else else
for (const sla::SupportPoint& point : m_c->m_model_object->sla_support_points) for (const sla::SupportPoint& point : mo->sla_support_points)
m_normal_cache.emplace_back(point); m_normal_cache.emplace_back(point);
} }
bool GLGizmoSlaSupports::has_backend_supports() const bool GLGizmoSlaSupports::has_backend_supports() const
{ {
if (! m_c->m_model_object) const ModelObject* mo = m_c->selection_info()->model_object();
if (! mo)
return false; return false;
// find SlaPrintObject with this ID // find SlaPrintObject with this ID
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == m_c->m_model_object->id()) if (po->model_object()->id() == mo->id())
return po->is_step_done(slaposSupportPoints); return po->is_step_done(slaposSupportPoints);
} }
return false; return false;
@ -1207,24 +1088,28 @@ bool GLGizmoSlaSupports::has_backend_supports() const
void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const
{ {
wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_c->m_model_object, postpone_error_messages); }); wxGetApp().CallAfter([this, postpone_error_messages]() {
wxGetApp().plater()->reslice_SLA_supports(
*m_c->selection_info()->model_object(), postpone_error_messages);
});
} }
void GLGizmoSlaSupports::get_data_from_backend() void GLGizmoSlaSupports::get_data_from_backend()
{ {
if (! has_backend_supports()) if (! has_backend_supports())
return; return;
ModelObject* mo = m_c->selection_info()->model_object();
// find the respective SLAPrintObject, we need a pointer to it // find the respective SLAPrintObject, we need a pointer to it
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == m_c->m_model_object->id()) { if (po->model_object()->id() == mo->id()) {
m_normal_cache.clear(); m_normal_cache.clear();
const std::vector<sla::SupportPoint>& points = po->get_support_points(); const std::vector<sla::SupportPoint>& points = po->get_support_points();
auto mat = po->trafo().inverse().cast<float>(); auto mat = po->trafo().inverse().cast<float>();
for (unsigned int i=0; i<points.size();++i) for (unsigned int i=0; i<points.size();++i)
m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island)); m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island));
m_c->m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated; mo->sla_points_status = sla::PointsStatus::AutoGenerated;
break; break;
} }
} }
@ -1241,10 +1126,12 @@ void GLGizmoSlaSupports::auto_generate()
_(L("Are you sure you want to do it?")) + "\n", _(L("Are you sure you want to do it?")) + "\n",
_(L("Warning")), wxICON_WARNING | wxYES | wxNO); _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
if (m_c->m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { ModelObject* mo = m_c->selection_info()->model_object();
if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points")));
wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); wxGetApp().CallAfter([this]() { reslice_SLA_supports(); });
m_c->m_model_object->sla_points_status = sla::PointsStatus::Generating; mo->sla_points_status = sla::PointsStatus::Generating;
} }
} }
@ -1259,7 +1146,7 @@ void GLGizmoSlaSupports::switch_to_editing_mode()
m_editing_cache.emplace_back(sp); m_editing_cache.emplace_back(sp);
select_point(NoPoints); select_point(NoPoints);
m_parent.toggle_sla_auxiliaries_visibility(false, m_c->m_model_object, m_c->m_active_instance); m_c->instances_hider()->show_supports(false);
m_parent.set_as_dirty(); m_parent.set_as_dirty();
} }
@ -1269,7 +1156,7 @@ void GLGizmoSlaSupports::disable_editing_mode()
if (m_editing_mode) { if (m_editing_mode) {
m_editing_mode = false; m_editing_mode = false;
wxGetApp().plater()->leave_gizmos_stack(); wxGetApp().plater()->leave_gizmos_stack();
m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance); m_c->instances_hider()->show_supports(true);
m_parent.set_as_dirty(); m_parent.set_as_dirty();
} }
} }
@ -1288,26 +1175,6 @@ bool GLGizmoSlaSupports::unsaved_changes() const
return false; return false;
} }
void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const
{
if (! m_c->m_model_object)
return;
#if ENABLE_NON_STATIC_CANVAS_MANAGER
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
m_c->m_clipping_plane->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward());
#else
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward());
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift);
float dist = normal.dot(center);
*m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius));
m_parent.set_as_dirty();
}
SlaGizmoHelpDialog::SlaGizmoHelpDialog() SlaGizmoHelpDialog::SlaGizmoHelpDialog()
: wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) : wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
{ {

View File

@ -13,34 +13,17 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
class ClippingPlane;
class MeshClipper;
class MeshRaycaster;
class CommonGizmosData;
enum class SLAGizmoEventType : unsigned char; enum class SLAGizmoEventType : unsigned char;
class GLGizmoSlaSupports : public GLGizmoBase class GLGizmoSlaSupports : public GLGizmoBase
{ {
private: private:
//ModelObject* m_model_object = nullptr;
//ObjectID m_model_object_id = 0;
//int m_active_instance = -1;
//float m_active_instance_bb_radius; // to cache the bb
mutable double m_z_shift = 0.f;
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal); bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
const float RenderPointScale = 1.f; const float RenderPointScale = 1.f;
GLUquadricObj* m_quadric; GLUquadricObj* m_quadric;
typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned;
typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned;
//std::unique_ptr<MeshRaycaster> m_mesh_raycaster;
//const TriangleMesh* m_mesh;
const indexed_triangle_set* m_its;
//mutable int m_old_timestamp = -1;
//mutable int m_print_object_idx = -1;
//mutable int m_print_objects_count = -1;
class CacheEntry { class CacheEntry {
public: public:
@ -75,14 +58,12 @@ public:
void set_sla_support_data(ModelObject* model_object, const Selection& selection); void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points(bool force = false); void delete_selected_points(bool force = false);
ClippingPlane get_sla_clipping_plane() const; //ClippingPlane get_sla_clipping_plane() const;
bool is_in_editing_mode() const { return m_editing_mode; } bool is_in_editing_mode() const { return m_editing_mode; }
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
bool has_backend_supports() const; bool has_backend_supports() const;
void reslice_SLA_supports(bool postpone_error_messages = false) const; void reslice_SLA_supports(bool postpone_error_messages = false) const;
void update_clipping_plane(bool keep_normal = false) const;
void set_common_data_ptr(CommonGizmosData* ptr) { m_c = ptr; }
private: private:
bool on_init() override; bool on_init() override;
@ -90,9 +71,7 @@ private:
void on_render() const override; void on_render() const override;
void on_render_for_picking() const override; void on_render_for_picking() const override;
//void render_selection_rectangle() const;
void render_points(const Selection& selection, bool picking = false) const; void render_points(const Selection& selection, bool picking = false) const;
void render_clipping_plane(const Selection& selection) const;
bool unsaved_changes() const; bool unsaved_changes() const;
bool m_lock_unique_islands = false; bool m_lock_unique_islands = false;
@ -104,8 +83,7 @@ private:
float m_density_stash = 0.f; // and again float m_density_stash = 0.f; // and again
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected
std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
const ModelObject* m_old_mo = nullptr;
//std::unique_ptr<ClippingPlane> m_clipping_plane;
// This map holds all translated description texts, so they can be easily referenced during layout calculations // This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
@ -117,11 +95,6 @@ private:
bool m_selection_empty = true; bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
CommonGizmosData* m_c = nullptr;
//mutable std::unique_ptr<MeshClipper> m_object_clipper;
//mutable std::unique_ptr<MeshClipper> m_supports_clipper;
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const; std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
bool is_mesh_point_clipped(const Vec3d& point) const; bool is_mesh_point_clipped(const Vec3d& point) const;
bool is_point_in_hole(const Vec3f& pt) const; bool is_point_in_hole(const Vec3f& pt) const;
@ -142,7 +115,6 @@ private:
void auto_generate(); void auto_generate();
void switch_to_editing_mode(); void switch_to_editing_mode();
void disable_editing_mode(); void disable_editing_mode();
void reset_clipping_plane_normal() const;
protected: protected:
void on_set_state() override; void on_set_state() override;
@ -159,6 +131,7 @@ protected:
std::string on_get_name() const override; std::string on_get_name() const 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;
virtual CommonGizmosDataID on_get_requirements() const override;
void on_load(cereal::BinaryInputArchive& ar) override; void on_load(cereal::BinaryInputArchive& ar) override;
void on_save(cereal::BinaryOutputArchive& ar) const override; void on_save(cereal::BinaryOutputArchive& ar) const override;
}; };

View File

@ -31,6 +31,7 @@ enum class SLAGizmoEventType : unsigned char {
#include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp" #include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"

View File

@ -0,0 +1,473 @@
#include "GLGizmosCommon.hpp"
#include <cassert>
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Camera.hpp"
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
using namespace CommonGizmosDataObjects;
CommonGizmosDataPool::CommonGizmosDataPool(GLCanvas3D* canvas)
: m_canvas(canvas)
{
using c = CommonGizmosDataID;
m_data[c::SelectionInfo].reset( new SelectionInfo(this));
m_data[c::InstancesHider].reset( new InstancesHider(this));
m_data[c::HollowedMesh].reset( new HollowedMesh(this));
m_data[c::Raycaster].reset( new Raycaster(this));
m_data[c::ObjectClipper].reset( new ObjectClipper(this));
m_data[c::SupportsClipper].reset( new SupportsClipper(this));
}
void CommonGizmosDataPool::update(CommonGizmosDataID required)
{
assert(check_dependencies(required));
for (auto& [id, data] : m_data) {
if (int(required) & int(CommonGizmosDataID(id)))
data->update();
else
if (data->is_valid())
data->release();
}
}
SelectionInfo* CommonGizmosDataPool::selection_info() const
{
SelectionInfo* sel_info = dynamic_cast<SelectionInfo*>(m_data.at(CommonGizmosDataID::SelectionInfo).get());
assert(sel_info);
return sel_info->is_valid() ? sel_info : nullptr;
}
InstancesHider* CommonGizmosDataPool::instances_hider() const
{
InstancesHider* inst_hider = dynamic_cast<InstancesHider*>(m_data.at(CommonGizmosDataID::InstancesHider).get());
assert(inst_hider);
return inst_hider->is_valid() ? inst_hider : nullptr;
}
HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const
{
HollowedMesh* hol_mesh = dynamic_cast<HollowedMesh*>(m_data.at(CommonGizmosDataID::HollowedMesh).get());
assert(hol_mesh);
return hol_mesh->is_valid() ? hol_mesh : nullptr;
}
Raycaster* CommonGizmosDataPool::raycaster() const
{
Raycaster* rc = dynamic_cast<Raycaster*>(m_data.at(CommonGizmosDataID::Raycaster).get());
assert(rc);
return rc->is_valid() ? rc : nullptr;
}
ObjectClipper* CommonGizmosDataPool::object_clipper() const
{
ObjectClipper* oc = dynamic_cast<ObjectClipper*>(m_data.at(CommonGizmosDataID::ObjectClipper).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;
}
SupportsClipper* CommonGizmosDataPool::supports_clipper() const
{
SupportsClipper* sc = dynamic_cast<SupportsClipper*>(m_data.at(CommonGizmosDataID::SupportsClipper).get());
assert(sc);
return sc->is_valid() ? sc : nullptr;
}
#ifndef NDEBUG
// Check the required resources one by one and return true if all
// dependencies are met.
bool CommonGizmosDataPool::check_dependencies(CommonGizmosDataID 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(CommonGizmosDataID(id))))
continue;
CommonGizmosDataID deps = data->get_dependencies();
assert(int(deps) == (int(deps) & int(required)));
}
return true;
}
#endif // NDEBUG
void SelectionInfo::on_update()
{
const Selection& selection = get_pool()->get_canvas()->get_selection();
if (selection.is_single_full_instance()) {
m_model_object = selection.get_model()->objects[selection.get_object_idx()];
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
}
else
m_model_object = nullptr;
}
void SelectionInfo::on_release()
{
m_model_object = nullptr;
}
int SelectionInfo::get_active_instance() const
{
const Selection& selection = get_pool()->get_canvas()->get_selection();
return selection.get_instance_idx();
}
void InstancesHider::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
int active_inst = get_pool()->selection_info()->get_active_instance();
GLCanvas3D* canvas = get_pool()->get_canvas();
if (mo && active_inst != -1) {
canvas->toggle_model_objects_visibility(false);
canvas->toggle_model_objects_visibility(true, mo, active_inst);
canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst);
}
else
canvas->toggle_model_objects_visibility(true);
}
void InstancesHider::on_release()
{
get_pool()->get_canvas()->toggle_model_objects_visibility(true);
}
void InstancesHider::show_supports(bool show) {
if (m_show_supports != show) {
m_show_supports = show;
on_update();
}
}
void HollowedMesh::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
if (! mo)
return;
const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = m_print_object_idx != -1
? print_objects[m_print_object_idx]
: nullptr;
// Find the respective SLAPrintObject.
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
m_print_objects_count = print_objects.size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : print_objects) {
++m_print_object_idx;
if (po->model_object()->id() == mo->id()) {
print_object = po;
break;
}
}
}
// If there is a valid SLAPrintObject, check state of Hollowing step.
if (print_object) {
if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) {
size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp;
if (timestamp > m_old_hollowing_timestamp) {
const TriangleMesh& backend_mesh = print_object->get_mesh_to_print();
if (! backend_mesh.empty()) {
m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse();
m_hollowed_mesh_transformed->transform(trafo_inv);
m_old_hollowing_timestamp = timestamp;
}
else
m_hollowed_mesh_transformed.reset(nullptr);
}
}
else
m_hollowed_mesh_transformed.reset(nullptr);
}
}
void HollowedMesh::on_release()
{
m_hollowed_mesh_transformed.reset();
m_old_hollowing_timestamp = 0;
m_print_object_idx = -1;
}
const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
{
return m_hollowed_mesh_transformed.get();
}
void Raycaster::on_update()
{
wxBusyCursor wait;
const ModelObject* mo = get_pool()->selection_info()->model_object();
if (! mo)
return;
std::vector<const TriangleMesh*> meshes;
const std::vector<ModelVolume*>& mvs = mo->volumes;
if (mvs.size() == 1) {
assert(mvs.front()->is_model_part());
const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh();
if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh())
meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh());
}
if (meshes.empty()) {
for (const ModelVolume* mv : mvs) {
if (mv->is_model_part())
meshes.push_back(&mv->mesh());
}
}
if (meshes != m_old_meshes) {
m_raycasters.clear();
for (const TriangleMesh* mesh : meshes)
m_raycasters.emplace_back(new MeshRaycaster(*mesh));
m_old_meshes = meshes;
}
}
void Raycaster::on_release()
{
m_raycasters.clear();
m_old_meshes.clear();
}
std::vector<const MeshRaycaster*> Raycaster::raycasters() const
{
std::vector<const MeshRaycaster*> mrcs;
for (const auto& raycaster_unique_ptr : m_raycasters)
mrcs.push_back(raycaster_unique_ptr.get());
return mrcs;
}
void ObjectClipper::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
if (! mo)
return;
// which mesh should be cut?
std::vector<const TriangleMesh*> meshes;
bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh();
if (has_hollowed)
meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh());
if (meshes.empty()) {
for (const ModelVolume* mv : mo->volumes)
if (mv->is_model_part())
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);
}
m_old_meshes = meshes;
m_active_inst_bb_radius =
mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
//if (has_hollowed && m_clp_ratio != 0.)
// m_clp_ratio = 0.25;
}
}
void ObjectClipper::on_release()
{
m_clippers.clear();
m_old_meshes.clear();
m_clp.reset();
m_clp_ratio = 0.;
}
void ObjectClipper::render_cut() const
{
if (m_clp_ratio == 0.)
return;
const SelectionInfo* sel_info = get_pool()->selection_info();
const ModelObject* mo = sel_info->model_object();
Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
size_t clipper_id = 0;
for (const ModelVolume* mv : mo->volumes) {
if (! mv->is_model_part())
continue;
Geometry::Transformation vol_trafo = mv->get_transformation();
Geometry::Transformation trafo = inst_trafo * vol_trafo;
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
auto& clipper = m_clippers[clipper_id];
clipper->set_plane(*m_clp);
clipper->set_transformation(trafo);
if (! clipper->get_triangles().empty()) {
::glPushMatrix();
::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
++clipper_id;
}
}
void ObjectClipper::set_position(double pos, bool keep_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 = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward();
const Vec3d& center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift);
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)));
get_pool()->get_canvas()->set_as_dirty();
}
void SupportsClipper::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
if (! mo)
return;
const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = m_print_object_idx != -1
? print_objects[m_print_object_idx]
: nullptr;
// Find the respective SLAPrintObject.
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
m_print_objects_count = print_objects.size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : print_objects) {
++m_print_object_idx;
if (po->model_object()->id() == mo->id()) {
print_object = po;
break;
}
}
}
if (print_object
&& print_object->is_step_done(slaposSupportTree)
&& ! print_object->support_mesh().empty())
{
// If the supports are already calculated, save the timestamp of the respective step
// so we can later tell they were recalculated.
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
if (! m_clipper || timestamp != m_old_timestamp) {
// The timestamp has changed.
m_clipper.reset(new MeshClipper);
// The mesh should already have the shared vertices calculated.
m_clipper->set_mesh(print_object->support_mesh());
m_old_timestamp = timestamp;
}
}
else
// The supports are not valid. We better dump the cached data.
m_clipper.reset();
}
void SupportsClipper::on_release()
{
m_clipper.reset();
m_old_timestamp = 0;
m_print_object_idx = -1;
}
void SupportsClipper::render_cut() const
{
const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper();
if (ocl->get_position() == 0.
|| ! get_pool()->instances_hider()->are_supports_shown()
|| ! m_clipper)
return;
const SelectionInfo* sel_info = get_pool()->selection_info();
const ModelObject* mo = sel_info->model_object();
Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
//Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation();
Geometry::Transformation trafo = inst_trafo;// * vol_trafo;
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
// Get transformation of supports
Geometry::Transformation supports_trafo = trafo;
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift()));
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
// I don't know why, but following seems to be correct.
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
1,
1.));
m_clipper->set_plane(*ocl->get_clipping_plane());
m_clipper->set_transformation(supports_trafo);
if (! m_clipper->get_triangles().empty()) {
::glPushMatrix();
::glColor3f(1.0f, 0.f, 0.37f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,309 @@
#ifndef slic3r_GUI_GLGizmosCommon_hpp_
#define slic3r_GUI_GLGizmosCommon_hpp_
#include <memory>
#include <map>
#include "slic3r/GUI/MeshUtils.hpp"
namespace Slic3r {
class ModelObject;
namespace GUI {
class GLCanvas3D;
static constexpr float HoleStickOutLength = 1.f;
enum class SLAGizmoEventType : unsigned char {
LeftDown = 1,
LeftUp,
RightDown,
RightUp,
Dragging,
Delete,
SelectAll,
ShiftUp,
AltUp,
ApplyChanges,
DiscardChanges,
AutomaticGeneration,
ManualEditing,
MouseWheelUp,
MouseWheelDown,
ResetClippingPlane
};
class CommonGizmosDataBase;
namespace CommonGizmosDataObjects {
class SelectionInfo;
class InstancesHider;
class HollowedMesh;
class Raycaster;
class ObjectClipper;
class SupportsClipper;
}
// 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
// they are not in use by any gizmo anymore.
// Enumeration of various data types that the data pool can contain.
// Each gizmo can tell which of the data it wants to use through
// on_get_requirements() method.
enum class CommonGizmosDataID {
None = 0,
SelectionInfo = 1 << 0,
InstancesHider = 1 << 1,
HollowedMesh = 1 << 2,
Raycaster = 1 << 3,
ObjectClipper = 1 << 4,
SupportsClipper = 1 << 5,
};
// Following class holds pointers to the common data objects and triggers
// their updating/releasing. There is just one object of this type (managed
// by GLGizmoManager, the gizmos keep a pointer to it.
class CommonGizmosDataPool {
public:
CommonGizmosDataPool(GLCanvas3D* canvas);
// Update all resources and release what is not used.
// Accepts a bitmask of currently required resources.
void update(CommonGizmosDataID required);
// Getters for the data that need to be accessed from the gizmos directly.
CommonGizmosDataObjects::SelectionInfo* selection_info() const;
CommonGizmosDataObjects::InstancesHider* instances_hider() const;
CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const;
CommonGizmosDataObjects::Raycaster* raycaster() const;
CommonGizmosDataObjects::ObjectClipper* object_clipper() const;
CommonGizmosDataObjects::SupportsClipper* supports_clipper() const;
GLCanvas3D* get_canvas() const { return m_canvas; }
private:
std::map<CommonGizmosDataID, std::unique_ptr<CommonGizmosDataBase>> m_data;
GLCanvas3D* m_canvas;
#ifndef NDEBUG
bool check_dependencies(CommonGizmosDataID 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 CommonGizmosDataBase {
public:
// Pass a backpointer to the pool, so the individual
// objects can communicate with one another.
explicit CommonGizmosDataBase(CommonGizmosDataPool* cgdp)
: m_common{cgdp} {}
virtual ~CommonGizmosDataBase() {}
// 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 CommonGizmosDataID get_dependencies() const { return CommonGizmosDataID::None; }
#endif // NDEBUG
protected:
virtual void on_release() = 0;
virtual void on_update() = 0;
CommonGizmosDataPool* get_pool() const { return m_common; }
private:
bool m_is_valid = false;
CommonGizmosDataPool* m_common = nullptr;
};
// The specializations of the CommonGizmosDataBase class live in this
// namespace to avoid clashes in GUI namespace.
namespace CommonGizmosDataObjects
{
class SelectionInfo : public CommonGizmosDataBase
{
public:
explicit SelectionInfo(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
ModelObject* model_object() const { return m_model_object; }
int get_active_instance() const;
float get_sla_shift() const { return m_z_shift; }
protected:
void on_update() override;
void on_release() override;
private:
ModelObject* m_model_object = nullptr;
int m_active_inst = -1;
float m_z_shift = 0.f;
};
class InstancesHider : public CommonGizmosDataBase
{
public:
explicit InstancesHider(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
void show_supports(bool show);
bool are_supports_shown() const { return m_show_supports; }
protected:
void on_update() override;
void on_release() override;
private:
bool m_show_supports = false;
};
class HollowedMesh : public CommonGizmosDataBase
{
public:
explicit HollowedMesh(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
const TriangleMesh* get_hollowed_mesh() const;
protected:
void on_update() override;
void on_release() override;
private:
std::unique_ptr<TriangleMesh> m_hollowed_mesh_transformed;
size_t m_old_hollowing_timestamp = 0;
int m_print_object_idx = -1;
int m_print_objects_count = 0;
};
class Raycaster : public CommonGizmosDataBase
{
public:
explicit Raycaster(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
const MeshRaycaster* raycaster() const { assert(m_raycasters.size() == 1); return m_raycasters.front().get(); }
std::vector<const MeshRaycaster*> raycasters() const;
protected:
void on_update() override;
void on_release() override;
private:
std::vector<std::unique_ptr<MeshRaycaster>> m_raycasters;
std::vector<const TriangleMesh*> m_old_meshes;
};
class ObjectClipper : public CommonGizmosDataBase
{
public:
explicit ObjectClipper(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#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.;
};
class SupportsClipper : public CommonGizmosDataBase
{
public:
explicit SupportsClipper(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override {
return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::ObjectClipper)
);
}
#endif // NDEBUG
void render_cut() const;
protected:
void on_update() override;
void on_release() override;
private:
size_t m_old_timestamp = 0;
int m_print_object_idx = -1;
int m_print_objects_count = 0;
std::unique_ptr<MeshClipper> m_clipper;
};
} // namespace CommonGizmosDataObjects
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GUI_GLGizmosCommon_hpp_

View File

@ -9,10 +9,15 @@
#include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/Utils/UndoRedo.hpp" #include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMove.hpp"
#include "slic3r/GUI/Gizmos/GLGizmos.hpp" #include "slic3r/GUI/Gizmos/GLGizmoScale.hpp"
#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
#include <wx/glcanvas.h> #include <wx/glcanvas.h>
@ -97,16 +102,16 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4));
m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5)); m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5));
m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6));
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "sla_supports.svg", 7));
m_common_gizmos_data.reset(new CommonGizmosData()); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->set_common_data_ptr(m_common_gizmos_data.get());
dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->set_common_data_ptr(m_common_gizmos_data.get());
for (auto& gizmo : m_gizmos) { for (auto& gizmo : m_gizmos) {
if (! gizmo->init()) { if (! gizmo->init()) {
m_gizmos.clear(); m_gizmos.clear();
return false; return false;
} }
gizmo->set_common_data_pool(m_common_gizmos_data.get());
} }
m_current = Undefined; m_current = Undefined;
@ -198,6 +203,10 @@ void GLGizmosManager::update_data()
enable_grabber(Scale, i, enable_scale_xyz); enable_grabber(Scale, i, enable_scale_xyz);
} }
m_common_gizmos_data->update(get_current()
? get_current()->get_requirements()
: CommonGizmosDataID(0));
if (selection.is_single_full_instance()) if (selection.is_single_full_instance())
{ {
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
@ -207,6 +216,7 @@ void GLGizmosManager::update_data()
ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()]; ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()];
set_flattening_data(model_object); set_flattening_data(model_object);
set_sla_support_data(model_object); set_sla_support_data(model_object);
set_fdm_support_data(model_object);
} }
else if (selection.is_single_volume() || selection.is_single_modifier()) else if (selection.is_single_volume() || selection.is_single_modifier())
{ {
@ -215,6 +225,7 @@ void GLGizmosManager::update_data()
set_rotation(Vec3d::Zero()); set_rotation(Vec3d::Zero());
set_flattening_data(nullptr); set_flattening_data(nullptr);
set_sla_support_data(nullptr); set_sla_support_data(nullptr);
set_fdm_support_data(nullptr);
} }
else if (is_wipe_tower) else if (is_wipe_tower)
{ {
@ -223,6 +234,7 @@ void GLGizmosManager::update_data()
set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast<const ConfigOptionFloat*>(config.option("wipe_tower_rotation_angle"))->value)); set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast<const ConfigOptionFloat*>(config.option("wipe_tower_rotation_angle"))->value));
set_flattening_data(nullptr); set_flattening_data(nullptr);
set_sla_support_data(nullptr); set_sla_support_data(nullptr);
set_fdm_support_data(nullptr);
} }
else else
{ {
@ -230,6 +242,7 @@ void GLGizmosManager::update_data()
set_rotation(Vec3d::Zero()); set_rotation(Vec3d::Zero());
set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
set_fdm_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
} }
} }
@ -358,15 +371,27 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object)
|| wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
return; return;
m_common_gizmos_data->update_from_backend(m_parent, model_object); /*m_common_gizmos_data->update_from_backend(m_parent, model_object);
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get()); auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
auto* gizmo_hollow = dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get());
// note: sla support gizmo takes care of updating the common data. // note: sla support gizmo takes care of updating the common data.
// following lines are thus dependent // following lines are thus dependent
gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); //gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
*/
auto* gizmo_hollow = dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get());
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection()); gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection());
gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
}
void GLGizmosManager::set_fdm_support_data(ModelObject* model_object)
{
if (!m_enabled || m_gizmos.empty())
return;
dynamic_cast<GLGizmoFdmSupports*>(m_gizmos[FdmSupports].get())->set_fdm_support_data(model_object, m_parent.get_selection());
} }
// Returns true if the gizmo used the event to do something, false otherwise. // Returns true if the gizmo used the event to do something, false otherwise.
@ -377,20 +402,24 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
if (m_current == SlaSupports) if (m_current == SlaSupports)
return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
if (m_current == Hollow) else if (m_current == Hollow)
return dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
return false; else if (m_current == FdmSupports)
return dynamic_cast<GLGizmoFdmSupports*>(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else
return false;
} }
ClippingPlane GLGizmosManager::get_sla_clipping_plane() const ClippingPlane GLGizmosManager::get_clipping_plane() const
{ {
if (!m_enabled || (m_current != SlaSupports && m_current != Hollow) || m_gizmos.empty()) if (! m_common_gizmos_data
|| ! m_common_gizmos_data->object_clipper()
|| m_common_gizmos_data->object_clipper()->get_position() == 0.)
return ClippingPlane::ClipsNothing(); return ClippingPlane::ClipsNothing();
else {
if (m_current == SlaSupports) const ClippingPlane& clp = *m_common_gizmos_data->object_clipper()->get_clipping_plane();
return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); return ClippingPlane(-clp.get_normal(), clp.get_data()[3]);
else }
return dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->get_sla_clipping_plane();
} }
bool GLGizmosManager::wants_reslice_supports_on_undo() const bool GLGizmosManager::wants_reslice_supports_on_undo() const
@ -410,6 +439,7 @@ void GLGizmosManager::render_current_gizmo() const
void GLGizmosManager::render_current_gizmo_for_picking_pass() const void GLGizmosManager::render_current_gizmo_for_picking_pass() const
{ {
if (! m_enabled || m_current == Undefined) if (! m_enabled || m_current == Undefined)
return; return;
m_gizmos[m_current]->render_for_picking(); m_gizmos[m_current]->render_for_picking();
@ -439,7 +469,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
{ {
bool processed = false; bool processed = false;
if (m_current == SlaSupports || m_current == Hollow) { if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) {
float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
processed = true; processed = true;
@ -529,8 +559,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true; processed = true;
m_mouse_capture.right = false; m_mouse_capture.right = false;
} }
else // else
return false; // return false;
} }
#if ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX #if ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX
else if (evt.Dragging() && !is_dragging()) else if (evt.Dragging() && !is_dragging())
@ -618,7 +648,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
if (evt.LeftDown()) if (evt.LeftDown())
{ {
if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports)
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
// the gizmo got the event and took some action, there is no need to do anything more // the gizmo got the event and took some action, there is no need to do anything more
processed = true; processed = true;
else if (!selection.is_empty() && grabber_contains_mouse()) { else if (!selection.is_empty() && grabber_contains_mouse()) {
@ -636,17 +667,25 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true; processed = true;
} }
} }
else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::RightDown)) else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos))
{ {
// we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object
pending_right_up = true; pending_right_up = true;
// event was taken care of by the SlaSupports gizmo // event was taken care of by the SlaSupports gizmo
processed = true; processed = true;
} }
else if (evt.RightDown() && (selected_object_idx != -1) && m_current == FdmSupports
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos))
{
// event was taken care of by the FdmSupports gizmo
processed = true;
}
else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow))
// don't allow dragging objects with the Sla gizmo on // don't allow dragging objects with the Sla gizmo on
processed = true; processed = true;
else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports )
&& gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
{ {
// the gizmo got the event and took some action, no need to do anything more here // the gizmo got the event and took some action, no need to do anything more here
m_parent.set_as_dirty(); m_parent.set_as_dirty();
@ -723,9 +762,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true; processed = true;
} }
#endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI #endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI
else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow) && !m_parent.is_mouse_dragging()) else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && !m_parent.is_mouse_dragging())
{ {
// in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither
// object moving or selecting is suppressed in that case // object moving or selecting is suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
processed = true; processed = true;
@ -735,6 +774,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
processed = true; processed = true;
} }
else if (evt.RightUp() && m_current == FdmSupports && !m_parent.is_mouse_dragging())
{
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
processed = true;
}
} }
else else
{ {
@ -824,7 +868,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
case 'r' : case 'r' :
case 'R' : case 'R' :
{ {
if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane))
processed = true; processed = true;
break; break;
@ -1188,10 +1232,13 @@ void GLGizmosManager::activate_gizmo(EType type)
return; // gizmo refused to be turned off, do nothing. return; // gizmo refused to be turned off, do nothing.
} }
m_current = type;
m_common_gizmos_data->update(get_current()
? get_current()->get_requirements()
: CommonGizmosDataID(0));
if (type != Undefined) if (type != Undefined)
m_gizmos[type]->set_state(GLGizmoBase::On); m_gizmos[type]->set_state(GLGizmoBase::On);
m_current = type;
} }
@ -1204,135 +1251,5 @@ bool GLGizmosManager::grabber_contains_mouse() const
return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; return (curr != nullptr) ? (curr->get_hover_id() != -1) : false;
} }
CommonGizmosData::CommonGizmosData()
{
m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
}
bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object)
{
recent_update = false;
bool object_changed = false;
if (m_model_object != model_object
|| (model_object && m_model_object_id != model_object->id())) {
m_model_object = model_object;
m_print_object_idx = -1;
m_mesh_raycaster.reset();
m_object_clipper.reset();
m_supports_clipper.reset();
m_old_mesh = nullptr;
m_mesh = nullptr;
m_backend_mesh_transformed.clear();
object_changed = true;
recent_update = true;
}
if (m_model_object) {
int active_inst = canvas.get_selection().get_instance_idx();
if (m_active_instance != active_inst) {
m_active_instance = active_inst;
m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius();
recent_update = true;
}
}
if (! m_model_object || ! canvas.get_selection().is_from_single_instance())
return false;
int old_po_idx = m_print_object_idx;
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
// cached so we don't have todo it on each render. We only search for the po if needed:
if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) {
m_print_objects_count = canvas.sla_print()->objects().size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : canvas.sla_print()->objects()) {
++m_print_object_idx;
if (po->model_object()->id() == m_model_object->id())
break;
}
}
bool mesh_exchanged = false;
m_mesh = nullptr;
// Load either the model_object mesh, or one provided by the backend
// This mesh does not account for the possible Z up SLA offset.
// The backend mesh needs to be transformed and because a pointer to it is
// saved, a copy is stored as a member (FIXME)
if (m_print_object_idx >=0) {
const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx];
if (po->is_step_done(slaposDrillHoles)) {
m_backend_mesh_transformed = po->get_mesh_to_print();
m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse());
m_mesh = &m_backend_mesh_transformed;
m_has_drilled_mesh = true;
mesh_exchanged = true;
}
}
if (! m_mesh) {
m_mesh = &m_model_object->volumes.front()->mesh();
m_backend_mesh_transformed.clear();
m_has_drilled_mesh = false;
}
m_model_object_id = m_model_object->id();
if (m_mesh != m_old_mesh) {
// Update clipping plane position.
float new_clp_pos = m_clipping_plane_distance;
if (object_changed) {
new_clp_pos = 0.f;
m_clipping_plane_was_moved = false;
} else {
// After we got a drilled mesh, move the cp to 25%. This only applies when
// the hollowing gizmo is active and hollowing is enabled
if (m_clipping_plane_distance == 0.f && mesh_exchanged && m_has_drilled_mesh) {
const DynamicPrintConfig& cfg =
(m_model_object && m_model_object->config.has("hollowing_enable"))
? m_model_object->config
: wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
if (cfg.has("hollowing_enable") && cfg.opt_bool("hollowing_enable")
&& canvas.get_gizmos_manager().get_current_type() == GLGizmosManager::Hollow) {
new_clp_pos = 0.25f;
m_clipping_plane_was_moved = false; // so it uses current camera direction
}
}
}
m_clipping_plane_distance = new_clp_pos;
m_clipping_plane_distance_stash = new_clp_pos;
m_schedule_aabb_calculation = true;
recent_update = true;
return true;
}
if (! recent_update)
recent_update = m_print_object_idx < 0 && old_po_idx >= 0;
return recent_update;
}
void CommonGizmosData::build_AABB_if_needed()
{
if (! m_schedule_aabb_calculation)
return;
wxBusyCursor wait;
m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh));
m_object_clipper.reset();
m_supports_clipper.reset();
m_old_mesh = m_mesh;
m_schedule_aabb_calculation = false;
}
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -3,8 +3,8 @@
#include "slic3r/GUI/GLTexture.hpp" #include "slic3r/GUI/GLTexture.hpp"
#include "slic3r/GUI/GLToolbar.hpp" #include "slic3r/GUI/GLToolbar.hpp"
#include "libslic3r/ObjectID.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoBase.hpp" #include "slic3r/GUI/Gizmos/GLGizmoBase.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include <map> #include <map>
@ -19,7 +19,7 @@ namespace GUI {
class GLCanvas3D; class GLCanvas3D;
class ClippingPlane; class ClippingPlane;
enum class SLAGizmoEventType : unsigned char; enum class SLAGizmoEventType : unsigned char;
class CommonGizmosData; class CommonGizmosDataPool;
class Rect class Rect
{ {
@ -64,6 +64,7 @@ public:
Cut, Cut,
Hollow, Hollow,
SlaSupports, SlaSupports,
FdmSupports,
Undefined Undefined
}; };
@ -115,7 +116,8 @@ private:
MouseCapture m_mouse_capture; MouseCapture m_mouse_capture;
std::string m_tooltip; std::string m_tooltip;
bool m_serializing; bool m_serializing;
std::unique_ptr<CommonGizmosData> m_common_gizmos_data; //std::unique_ptr<CommonGizmosData> m_common_gizmos_data;
std::unique_ptr<CommonGizmosDataPool> m_common_gizmos_data;
public: public:
explicit GLGizmosManager(GLCanvas3D& parent); explicit GLGizmosManager(GLCanvas3D& parent);
@ -197,8 +199,11 @@ public:
void set_flattening_data(const ModelObject* model_object); void set_flattening_data(const ModelObject* model_object);
void set_sla_support_data(ModelObject* model_object); void set_sla_support_data(ModelObject* model_object);
void set_fdm_support_data(ModelObject* model_object);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false);
ClippingPlane get_sla_clipping_plane() const; ClippingPlane get_clipping_plane() const;
bool wants_reslice_supports_on_undo() const; bool wants_reslice_supports_on_undo() const;
void render_current_gizmo() const; void render_current_gizmo() const;
@ -231,63 +236,6 @@ private:
class MeshRaycaster;
class MeshClipper;
// This class is only for sharing SLA related data between SLA gizmos
// and its synchronization with backend data. It should not be misused
// for anything else.
class CommonGizmosData {
public:
CommonGizmosData();
const TriangleMesh* mesh() const {
return (! m_mesh ? nullptr : m_mesh); //(m_cavity_mesh ? m_cavity_mesh.get() : m_mesh));
}
bool update_from_backend(GLCanvas3D& canvas, ModelObject* model_object);
bool recent_update = false;
static constexpr float HoleStickOutLength = 1.f;
ModelObject* m_model_object = nullptr;
const TriangleMesh* m_mesh;
std::unique_ptr<MeshRaycaster> m_mesh_raycaster;
std::unique_ptr<MeshClipper> m_object_clipper;
std::unique_ptr<MeshClipper> m_supports_clipper;
//std::unique_ptr<TriangleMesh> m_cavity_mesh;
//std::unique_ptr<GLVolume> m_volume_with_cavity;
int m_active_instance = -1;
float m_active_instance_bb_radius = 0;
ObjectID m_model_object_id = 0;
int m_print_object_idx = -1;
int m_print_objects_count = -1;
int m_old_timestamp = -1;
float m_clipping_plane_distance = 0.f;
std::unique_ptr<ClippingPlane> m_clipping_plane;
bool m_clipping_plane_was_moved = false;
void stash_clipping_plane() {
m_clipping_plane_distance_stash = m_clipping_plane_distance;
}
void unstash_clipping_plane() {
m_clipping_plane_distance = m_clipping_plane_distance_stash;
}
bool has_drilled_mesh() const { return m_has_drilled_mesh; }
void build_AABB_if_needed();
private:
const TriangleMesh* m_old_mesh;
TriangleMesh m_backend_mesh_transformed;
float m_clipping_plane_distance_stash = 0.f;
bool m_has_drilled_mesh = false;
bool m_schedule_aabb_calculation = false;
};
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -85,19 +85,25 @@ void MeshClipper::recalculate_triangles()
tr = m_trafo.get_matrix().cast<float>() * tr; tr = m_trafo.get_matrix().cast<float>() * tr;
m_triangles3d.clear(); m_triangles3d.clear();
m_triangles3d.reserve(m_triangles2d.size()); m_triangles3d.reserve(m_triangles2d.size());
for (const Vec2f& pt : m_triangles2d) { for (const Vec2f& pt : m_triangles2d) {
m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f)); m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f));
m_triangles3d.back() = tr * m_triangles3d.back(); m_triangles3d.back() = tr * m_triangles3d.back();
} }
m_triangles_valid = true; m_triangles_valid = true;
} }
Vec3f MeshRaycaster::get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx)
{
Vec3f a(its.vertices[its.indices[facet_idx](1)] - its.vertices[its.indices[facet_idx](0)]);
Vec3f b(its.vertices[its.indices[facet_idx](2)] - its.vertices[its.indices[facet_idx](0)]);
return Vec3f(a.cross(b)).normalized();
}
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const Vec3d& point, Vec3d& direction) const
{ {
const std::array<int, 4>& viewport = camera.get_viewport(); const std::array<int, 4>& viewport = camera.get_viewport();
const Transform3d& model_mat = camera.get_view_matrix(); const Transform3d& model_mat = camera.get_view_matrix();
@ -112,7 +118,21 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
pt1 = inv * pt1; pt1 = inv * pt1;
pt2 = inv * pt2; pt2 = inv * pt2;
std::vector<sla::EigenMesh3D::hit_result> hits = m_emesh.query_ray_hits(pt1, pt2-pt1); point = pt1;
direction = pt2-pt1;
}
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane,
size_t* facet_idx) const
{
Vec3d point;
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
std::vector<sla::EigenMesh3D::hit_result> hits = m_emesh.query_ray_hits(point, direction);
if (hits.empty()) if (hits.empty())
return false; // no intersection found return false; // no intersection found
@ -134,6 +154,10 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
// Now stuff the points in the provided vector and calculate normals if asked about them: // Now stuff the points in the provided vector and calculate normals if asked about them:
position = hits[i].position().cast<float>(); position = hits[i].position().cast<float>();
normal = hits[i].normal().cast<float>(); normal = hits[i].normal().cast<float>();
if (facet_idx)
*facet_idx = hits[i].face();
return true; return true;
} }

View File

@ -4,6 +4,8 @@
#include "libslic3r/Point.hpp" #include "libslic3r/Point.hpp"
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/SLA/EigenMesh3D.hpp" #include "libslic3r/SLA/EigenMesh3D.hpp"
#include "admesh/stl.h"
#include <cfloat> #include <cfloat>
@ -26,10 +28,7 @@ class ClippingPlane
public: public:
ClippingPlane() ClippingPlane()
{ {
m_data[0] = 0.0; *this = ClipsNothing();
m_data[1] = 0.0;
m_data[2] = 1.0;
m_data[3] = 0.0;
} }
ClippingPlane(const Vec3d& direction, double offset) ClippingPlane(const Vec3d& direction, double offset)
@ -111,6 +110,9 @@ public:
: m_emesh(mesh) : m_emesh(mesh)
{} {}
void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction) const;
// Given a mouse position, this returns true in case it is on the mesh. // Given a mouse position, this returns true in case it is on the mesh.
bool unproject_on_mesh( bool unproject_on_mesh(
const Vec2d& mouse_pos, const Vec2d& mouse_pos,
@ -118,7 +120,8 @@ public:
const Camera& camera, // current camera position const Camera& camera, // current camera position
Vec3f& position, // where to save the positibon of the hit (mesh coords) Vec3f& position, // where to save the positibon of the hit (mesh coords)
Vec3f& normal, // normal of the triangle that was hit Vec3f& normal, // normal of the triangle that was hit
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active) const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
size_t* facet_idx = nullptr // index of the facet hit
) const; ) const;
// Given a vector of points in woorld coordinates, this returns vector // Given a vector of points in woorld coordinates, this returns vector
@ -134,8 +137,11 @@ public:
// Given a point in world coords, the method returns closest point on the mesh. // Given a point in world coords, the method returns closest point on the mesh.
// The output is in mesh coords. // The output is in mesh coords.
// normal* can be used to also get normal of the respective triangle. // normal* can be used to also get normal of the respective triangle.
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
static Vec3f get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx);
private: private:
sla::EigenMesh3D m_emesh; sla::EigenMesh3D m_emesh;
}; };

View File

@ -754,7 +754,8 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) :
std::vector < std::pair < wxString, std::string >> buttons = { std::vector < std::pair < wxString, std::string >> buttons = {
{_(L("Simple")), "mode_simple"}, {_(L("Simple")), "mode_simple"},
{_(L("Advanced")), "mode_advanced"}, // {_(L("Advanced")), "mode_advanced"},
{_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"},
{_(L("Expert")), "mode_expert"}, {_(L("Expert")), "mode_expert"},
}; };