Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-06-19 17:08:53 +0300
committerbubnikv <bubnikv@gmail.com>2018-06-19 17:08:53 +0300
commit5a56f08aad20237cc6ce0fe95abf06e9279d4096 (patch)
tree79f49e6d9d44548d1c7540b455f5eeac44b2d2e6 /xs
parent734273a33a7059ae2410f4d012432bfbf2a64b26 (diff)
parenta3949b9f01f60ad3f0543de270f4dc662f6c9249 (diff)
Merge remote-tracking branch 'remotes/origin/opengl_to_cpp'
Diffstat (limited to 'xs')
-rw-r--r--xs/CMakeLists.txt14
-rw-r--r--xs/lib/Slic3r/XS.pm3
-rw-r--r--xs/src/libslic3r/BoundingBox.cpp8
-rw-r--r--xs/src/libslic3r/BoundingBox.hpp1
-rw-r--r--xs/src/libslic3r/Model.cpp5
-rw-r--r--xs/src/libslic3r/Model.hpp11
-rw-r--r--xs/src/libslic3r/Point.hpp5
-rw-r--r--xs/src/libslic3r/Print.hpp2
-rw-r--r--xs/src/libslic3r/PrintObject.cpp9
-rw-r--r--xs/src/libslic3r/Utils.hpp11
-rw-r--r--xs/src/libslic3r/utils.cpp52
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp1238
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp184
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp4308
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp639
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.cpp707
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.hpp167
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.cpp454
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.hpp145
-rw-r--r--xs/src/slic3r/GUI/GLShader.cpp40
-rw-r--r--xs/src/slic3r/GUI/GLShader.hpp4
-rw-r--r--xs/src/slic3r/GUI/GLTexture.cpp185
-rw-r--r--xs/src/slic3r/GUI/GLTexture.hpp41
-rw-r--r--xs/xsp/GUI_3DScene.xsp518
-rw-r--r--xs/xsp/Print.xsp12
25 files changed, 7712 insertions, 1051 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 05293a523..117af1959 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -188,7 +188,15 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/3DScene.cpp
${LIBDIR}/slic3r/GUI/3DScene.hpp
${LIBDIR}/slic3r/GUI/GLShader.cpp
- ${LIBDIR}/slic3r/GUI/GLShader.hpp
+ ${LIBDIR}/slic3r/GUI/GLShader.hpp
+ ${LIBDIR}/slic3r/GUI/GLCanvas3D.hpp
+ ${LIBDIR}/slic3r/GUI/GLCanvas3D.cpp
+ ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.hpp
+ ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.cpp
+ ${LIBDIR}/slic3r/GUI/GLGizmo.hpp
+ ${LIBDIR}/slic3r/GUI/GLGizmo.cpp
+ ${LIBDIR}/slic3r/GUI/GLTexture.hpp
+ ${LIBDIR}/slic3r/GUI/GLTexture.cpp
${LIBDIR}/slic3r/GUI/Preferences.cpp
${LIBDIR}/slic3r/GUI/Preferences.hpp
${LIBDIR}/slic3r/GUI/Preset.cpp
@@ -566,13 +574,13 @@ if (SLIC3R_PRUSACONTROL)
set(wxWidgets_UseAlienWx 1)
if (wxWidgets_UseAlienWx)
set(AlienWx_DEBUG 1)
- find_package(AlienWx REQUIRED COMPONENTS base core adv html)
+ find_package(AlienWx REQUIRED COMPONENTS base core adv html gl)
include_directories(${AlienWx_INCLUDE_DIRS})
#add_compile_options(${AlienWx_CXX_FLAGS})
add_definitions(${AlienWx_DEFINITIONS})
set(wxWidgets_LIBRARIES ${AlienWx_LIBRARIES})
else ()
- find_package(wxWidgets REQUIRED COMPONENTS base core adv html)
+ find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl)
include(${wxWidgets_USE_FILE})
endif ()
add_definitions(-DSLIC3R_GUI -DSLIC3R_PRUS)
diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm
index 06eb041df..a4847fb45 100644
--- a/xs/lib/Slic3r/XS.pm
+++ b/xs/lib/Slic3r/XS.pm
@@ -12,6 +12,8 @@ our $VERSION = '0.01';
BEGIN {
if ($^O eq 'MSWin32') {
eval "use Wx";
+ eval "use Wx::GLCanvas";
+ eval "use Wx::GLContext";
eval "use Wx::Html";
eval "use Wx::Print"; # because of some Wx bug, thread creation fails if we don't have this (looks like Wx::Printout is hard-coded in some thread cleanup code)
}
@@ -280,6 +282,7 @@ for my $class (qw(
Slic3r::Geometry::BoundingBox
Slic3r::Geometry::BoundingBoxf
Slic3r::Geometry::BoundingBoxf3
+ Slic3r::GUI::_3DScene::GLShader
Slic3r::GUI::_3DScene::GLVolume
Slic3r::GUI::Preset
Slic3r::GUI::PresetCollection
diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp
index 91ba88d84..ceb968a50 100644
--- a/xs/src/libslic3r/BoundingBox.cpp
+++ b/xs/src/libslic3r/BoundingBox.cpp
@@ -222,6 +222,14 @@ BoundingBox3Base<PointClass>::center() const
}
template Pointf3 BoundingBox3Base<Pointf3>::center() const;
+template <class PointClass> coordf_t
+BoundingBox3Base<PointClass>::max_size() const
+{
+ PointClass s = size();
+ return std::max(s.x, std::max(s.y, s.z));
+}
+template coordf_t BoundingBox3Base<Pointf3>::max_size() const;
+
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index 92a2bd451..5de94aa9c 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -94,6 +94,7 @@ public:
void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); }
void offset(coordf_t delta);
PointClass center() const;
+ coordf_t max_size() const;
bool contains(const PointClass &point) const {
return BoundingBoxBase<PointClass>::contains(point) && point.z >= this->min.z && point.z <= this->max.z;
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 8ce23b1e5..147353abd 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -603,7 +603,10 @@ void ModelObject::clear_instances()
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
-const BoundingBoxf3& ModelObject::bounding_box()
+//========================================================================================================
+const BoundingBoxf3& ModelObject::bounding_box() const
+//const BoundingBoxf3& ModelObject::bounding_box()
+//========================================================================================================
{
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index 8b63c3641..b148ec29d 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -103,7 +103,10 @@ public:
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
// This bounding box is being cached.
- const BoundingBoxf3& bounding_box();
+//========================================================================================================
+ const BoundingBoxf3& bounding_box() const;
+// const BoundingBoxf3& bounding_box();
+//========================================================================================================
void invalidate_bounding_box() { m_bounding_box_valid = false; }
// Returns a snug bounding box of the transformed instances.
// This bounding box is not being cached.
@@ -145,8 +148,10 @@ private:
// Parent object, owning this ModelObject.
Model *m_model;
// Bounding box, cached.
- BoundingBoxf3 m_bounding_box;
- bool m_bounding_box_valid;
+//========================================================================================================
+ mutable BoundingBoxf3 m_bounding_box;
+ mutable bool m_bounding_box_valid;
+//========================================================================================================
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index 6c9096a3d..a52cdceb6 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -238,6 +238,11 @@ inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v2.x + v
inline coordf_t dot(const Pointf &v) { return v.x * v.x + v.y * v.y; }
inline double length(const Vectorf &v) { return sqrt(dot(v)); }
inline double l2(const Vectorf &v) { return dot(v); }
+inline Vectorf normalize(const Vectorf& v)
+{
+ coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y));
+ return (len != 0.0) ? 1.0 / len * v : Vectorf(0.0, 0.0);
+}
class Pointf3 : public Pointf
{
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index c56e64c6c..86c15b679 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -184,6 +184,8 @@ public:
void reset_layer_height_profile();
+ void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action);
+
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
// by the interactive layer height editor and by the printing process itself.
// The slicing parameters are dependent on various configuration values
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index b0341db16..ba0876a85 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -4,6 +4,7 @@
#include "Geometry.hpp"
#include "SupportMaterial.hpp"
#include "Surface.hpp"
+#include "Slicing.hpp"
#include <utility>
#include <boost/log/trivial.hpp>
@@ -1961,4 +1962,12 @@ void PrintObject::reset_layer_height_profile()
this->model_object()->layer_height_profile_valid = false;
}
+void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
+{
+ update_layer_height_profile(_model_object->layer_height_profile);
+ Slic3r::adjust_layer_height_profile(slicing_parameters(), _model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
+ _model_object->layer_height_profile_valid = true;
+ layer_height_profile_valid = false;
+}
+
} // namespace Slic3r
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index e5157741e..921841a27 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -91,10 +91,13 @@ public:
~PerlCallback() { this->deregister_callback(); }
void register_callback(void *sv);
void deregister_callback();
- void call();
- void call(int i);
- void call(int i, int j);
-// void call(const std::vector<int> &ints);
+ void call() const;
+ void call(int i) const;
+ void call(int i, int j) const;
+ void call(const std::vector<int>& ints) const;
+ void call(double d) const;
+ void call(double x, double y) const;
+ void call(bool b) const;
private:
void *m_callback;
};
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 582488c5a..745d07fcd 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -184,7 +184,7 @@ void PerlCallback::deregister_callback()
}
}
-void PerlCallback::call()
+void PerlCallback::call() const
{
if (! m_callback)
return;
@@ -198,7 +198,7 @@ void PerlCallback::call()
LEAVE;
}
-void PerlCallback::call(int i)
+void PerlCallback::call(int i) const
{
if (! m_callback)
return;
@@ -213,7 +213,7 @@ void PerlCallback::call(int i)
LEAVE;
}
-void PerlCallback::call(int i, int j)
+void PerlCallback::call(int i, int j) const
{
if (! m_callback)
return;
@@ -229,8 +229,7 @@ void PerlCallback::call(int i, int j)
LEAVE;
}
-/*
-void PerlCallback::call(const std::vector<int> &ints)
+void PerlCallback::call(const std::vector<int>& ints) const
{
if (! m_callback)
return;
@@ -238,16 +237,51 @@ void PerlCallback::call(const std::vector<int> &ints)
ENTER;
SAVETMPS;
PUSHMARK(SP);
- AV* av = newAV();
for (int i : ints)
- av_push(av, newSViv(i));
- XPUSHs(av);
+ {
+ XPUSHs(sv_2mortal(newSViv(i)));
+ }
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double d) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(d)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double x, double y) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(x)));
+ XPUSHs(sv_2mortal(newSVnv(y)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
-*/
+
+void PerlCallback::call(bool b) const
+{
+ call(b ? 1 : 0);
+}
#ifdef WIN32
#ifndef NOMINMAX
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index cb2e08e1f..1879b3082 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -1348,8 +1348,8 @@ static void point_to_indexed_vertex_array(const Point3& point,
volume.push_triangle(idxs[0], idxs[3], idxs[4]);
}
-static void thick_lines_to_verts(
- const Lines &lines,
+void _3DScene::thick_lines_to_verts(
+ const Lines &lines,
const std::vector<double> &widths,
const std::vector<double> &heights,
bool closed,
@@ -1359,7 +1359,7 @@ static void thick_lines_to_verts(
thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array);
}
-static void thick_lines_to_verts(const Lines3& lines,
+void _3DScene::thick_lines_to_verts(const Lines3& lines,
const std::vector<double>& widths,
const std::vector<double>& heights,
bool closed,
@@ -1377,7 +1377,7 @@ static void thick_point_to_verts(const Point3& point,
}
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
-static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume)
+void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume)
{
Lines lines = extrusion_path.polyline.lines();
std::vector<double> widths(lines.size(), extrusion_path.width);
@@ -1386,7 +1386,7 @@ static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path,
}
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
-static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point &copy, GLVolume &volume)
+void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point &copy, GLVolume &volume)
{
Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
@@ -1398,7 +1398,7 @@ static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path,
}
// Fill in the qverts and tverts with quads and triangles for the extrusion_loop.
-static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point &copy, GLVolume &volume)
+void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point &copy, GLVolume &volume)
{
Lines lines;
std::vector<double> widths;
@@ -1416,7 +1416,7 @@ static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop,
}
// Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path.
-static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point &copy, GLVolume &volume)
+void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point &copy, GLVolume &volume)
{
Lines lines;
std::vector<double> widths;
@@ -1433,15 +1433,13 @@ static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_
thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
}
-static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point &copy, GLVolume &volume);
-
-static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point &copy, GLVolume &volume)
+void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point &copy, GLVolume &volume)
{
for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities)
extrusionentity_to_verts(extrusion_entity, print_z, copy, volume);
}
-static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point &copy, GLVolume &volume)
+void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point &copy, GLVolume &volume)
{
if (extrusion_entity != nullptr) {
auto *extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity);
@@ -1468,7 +1466,7 @@ static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, fl
}
}
-static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume)
+void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume)
{
Lines3 lines = polyline.lines();
std::vector<double> widths(lines.size(), width);
@@ -1476,14 +1474,14 @@ static void polyline3_to_verts(const Polyline3& polyline, double width, double h
thick_lines_to_verts(lines, widths, heights, false, volume);
}
-static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume)
+void _3DScene::point3_to_verts(const Point3& point, double width, double height, GLVolume& volume)
{
thick_point_to_verts(point, width, height, volume);
}
-_3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index;
_3DScene::LegendTexture _3DScene::s_legend_texture;
_3DScene::WarningTexture _3DScene::s_warning_texture;
+GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
unsigned int _3DScene::TextureBase::finalize()
{
@@ -1720,1049 +1718,441 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con
return true;
}
-void _3DScene::_glew_init()
-{
- glewInit();
+void _3DScene::init_gl()
+{
+ s_canvas_mgr.init_gl();
}
-static inline int hex_digit_to_int(const char c)
+std::string _3DScene::get_gl_info(bool format_as_html, bool extensions)
{
- return
- (c >= '0' && c <= '9') ? int(c - '0') :
- (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
- (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
+ return s_canvas_mgr.get_gl_info(format_as_html, extensions);
}
-static inline std::vector<float> parse_colors(const std::vector<std::string> &scolors)
+bool _3DScene::use_VBOs()
{
- std::vector<float> output(scolors.size() * 4, 1.f);
- for (size_t i = 0; i < scolors.size(); ++ i) {
- const std::string &scolor = scolors[i];
- const char *c = scolor.data() + 1;
- if (scolor.size() == 7 && scolor.front() == '#') {
- for (size_t j = 0; j < 3; ++j) {
- int digit1 = hex_digit_to_int(*c ++);
- int digit2 = hex_digit_to_int(*c ++);
- if (digit1 == -1 || digit2 == -1)
- break;
- output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.f;
- }
- }
- }
- return output;
+ return s_canvas_mgr.use_VBOs();
}
-void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector<std::string>& str_tool_colors, bool use_VBOs)
+bool _3DScene::add_canvas(wxGLCanvas* canvas)
{
- if ((preview_data == nullptr) || (volumes == nullptr))
- return;
-
- if (volumes->empty())
- {
- std::vector<float> tool_colors = parse_colors(str_tool_colors);
-
- s_gcode_preview_volume_index.reset();
-
- _load_gcode_extrusion_paths(*preview_data, *volumes, tool_colors, use_VBOs);
- _load_gcode_travel_paths(*preview_data, *volumes, tool_colors, use_VBOs);
- _load_gcode_retractions(*preview_data, *volumes, use_VBOs);
- _load_gcode_unretractions(*preview_data, *volumes, use_VBOs);
-
- if (volumes->empty())
- reset_legend_texture();
- else
- {
- _generate_legend_texture(*preview_data, tool_colors);
-
- // removes empty volumes
- volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(),
- [](const GLVolume *volume) { return volume->print_zs.empty(); }),
- volumes->volumes.end());
-
- _load_shells(*print, *volumes, use_VBOs);
- }
- }
-
- _update_gcode_volumes_visibility(*preview_data, *volumes);
+ return s_canvas_mgr.add(canvas);
}
-unsigned int _3DScene::get_legend_texture_width()
+bool _3DScene::remove_canvas(wxGLCanvas* canvas)
{
- return s_legend_texture.get_texture_width();
+ return s_canvas_mgr.remove(canvas);
}
-unsigned int _3DScene::get_legend_texture_height()
+void _3DScene::remove_all_canvases()
{
- return s_legend_texture.get_texture_height();
+ s_canvas_mgr.remove_all();
}
-void _3DScene::reset_legend_texture()
+bool _3DScene::init(wxGLCanvas* canvas)
{
- s_legend_texture.reset_texture();
+ return s_canvas_mgr.init(canvas);
}
-unsigned int _3DScene::finalize_legend_texture()
+void _3DScene::set_active(wxGLCanvas* canvas, bool active)
{
- return s_legend_texture.finalize();
+ s_canvas_mgr.set_active(canvas, active);
}
-unsigned int _3DScene::get_warning_texture_width()
+unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas)
{
- return s_warning_texture.get_texture_width();
+ return s_canvas_mgr.get_volumes_count(canvas);
}
-unsigned int _3DScene::get_warning_texture_height()
+void _3DScene::reset_volumes(wxGLCanvas* canvas)
{
- return s_warning_texture.get_texture_height();
+ s_canvas_mgr.reset_volumes(canvas);
}
-void _3DScene::generate_warning_texture(const std::string& msg)
+void _3DScene::deselect_volumes(wxGLCanvas* canvas)
{
- s_warning_texture.generate(msg);
+ s_canvas_mgr.deselect_volumes(canvas);
}
-void _3DScene::reset_warning_texture()
+void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id)
{
- s_warning_texture.reset_texture();
+ s_canvas_mgr.select_volume(canvas, id);
}
-unsigned int _3DScene::finalize_warning_texture()
+void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections)
{
- return s_warning_texture.finalize();
+ s_canvas_mgr.update_volumes_selection(canvas, selections);
}
-// Create 3D thick extrusion lines for a skirt and brim.
-// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
-void _3DScene::_load_print_toolpaths(
- const Print *print,
- GLVolumeCollection *volumes,
- const std::vector<std::string> &tool_colors,
- bool use_VBOs)
+bool _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
{
- if (!print->has_skirt() && print->config.brim_width.value == 0)
- return;
-
- const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish
-
- // number of skirt layers
- size_t total_layer_count = 0;
- for (const PrintObject *print_object : print->objects)
- total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
- size_t skirt_height = print->has_infinite_skirt() ?
- total_layer_count :
- std::min<size_t>(print->config.skirt_height.value, total_layer_count);
- if (skirt_height == 0 && print->config.brim_width.value > 0)
- skirt_height = 1;
-
- // get first skirt_height layers (maybe this should be moved to a PrintObject method?)
- const PrintObject *object0 = print->objects.front();
- std::vector<float> print_zs;
- print_zs.reserve(skirt_height * 2);
- for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++ i)
- print_zs.push_back(float(object0->layers[i]->print_z));
- //FIXME why there are support layers?
- for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i)
- print_zs.push_back(float(object0->support_layers[i]->print_z));
- sort_remove_duplicates(print_zs);
- if (print_zs.size() > skirt_height)
- print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
-
- volumes->volumes.emplace_back(new GLVolume(color));
- GLVolume &volume = *volumes->volumes.back();
- for (size_t i = 0; i < skirt_height; ++ i) {
- volume.print_zs.push_back(print_zs[i]);
- volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
- volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
- if (i == 0)
- extrusionentity_to_verts(print->brim, print_zs[i], Point(0, 0), volume);
- extrusionentity_to_verts(print->skirt, print_zs[i], Point(0, 0), volume);
- }
- volume.bounding_box = volume.indexed_vertex_array.bounding_box();
- volume.indexed_vertex_array.finalize_geometry(use_VBOs);
+ return s_canvas_mgr.check_volumes_outside_state(canvas, config);
}
-// Create 3D thick extrusion lines for object forming extrusions.
-// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
-// one for perimeters, one for infill and one for supports.
-void _3DScene::_load_print_object_toolpaths(
- const PrintObject *print_object,
- GLVolumeCollection *volumes,
- const std::vector<std::string> &tool_colors_str,
- bool use_VBOs)
+bool _3DScene::move_volume_up(wxGLCanvas* canvas, unsigned int id)
{
- std::vector<float> tool_colors = parse_colors(tool_colors_str);
-
- struct Ctxt
- {
- const Points *shifted_copies;
- std::vector<const Layer*> layers;
- bool has_perimeters;
- bool has_infill;
- bool has_support;
- const std::vector<float>* tool_colors;
-
- // Number of vertices (each vertex is 6x4=24 bytes long)
- static const size_t alloc_size_max () { return 131072; } // 3.15MB
-// static const size_t alloc_size_max () { return 65536; } // 1.57MB
-// static const size_t alloc_size_max () { return 32768; } // 786kB
- static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
-
- static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
- static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
- static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
-
- // For cloring by a tool, return a parsed color.
- bool color_by_tool() const { return tool_colors != nullptr; }
- size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
- const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
- int volume_idx(int extruder, int feature) const
- { return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature; }
- } ctxt;
-
- ctxt.shifted_copies = &print_object->_shifted_copies;
-
- // order layers by print_z
- ctxt.layers.reserve(print_object->layers.size() + print_object->support_layers.size());
- for (const Layer *layer : print_object->layers)
- ctxt.layers.push_back(layer);
- for (const Layer *layer : print_object->support_layers)
- ctxt.layers.push_back(layer);
- std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
-
- // Maximum size of an allocation block: 32MB / sizeof(float)
- ctxt.has_perimeters = print_object->state.is_done(posPerimeters);
- ctxt.has_infill = print_object->state.is_done(posInfill);
- ctxt.has_support = print_object->state.is_done(posSupportMaterial);
- ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
-
- //FIXME Improve the heuristics for a grain size.
- size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
- tbb::spin_mutex new_volume_mutex;
- auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* {
- auto *volume = new GLVolume(color);
- new_volume_mutex.lock();
- volume->outside_printer_detection_enabled = false;
- volumes->volumes.emplace_back(volume);
- new_volume_mutex.unlock();
- return volume;
- };
- const size_t volumes_cnt_initial = volumes->volumes.size();
- std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
- std::vector<GLVolume*> vols;
- if (ctxt.color_by_tool()) {
- for (size_t i = 0; i < ctxt.number_tools(); ++ i)
- vols.emplace_back(new_volume(ctxt.color_tool(i)));
- } else
- vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
- for (GLVolume *vol : vols)
- vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
- const Layer *layer = ctxt.layers[idx_layer];
- for (size_t i = 0; i < vols.size(); ++ i) {
- GLVolume &vol = *vols[i];
- if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
- vol.print_zs.push_back(layer->print_z);
- vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
- vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
- }
- }
- for (const Point &copy: *ctxt.shifted_copies) {
- for (const LayerRegion *layerm : layer->regions) {
- if (ctxt.has_perimeters)
- extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
- if (ctxt.has_infill) {
- for (const ExtrusionEntity *ee : layerm->fills.entities) {
- // fill represents infill extrusions of a single island.
- const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (! fill->entities.empty())
- extrusionentity_to_verts(*fill, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(
- is_solid_infill(fill->entities.front()->role()) ?
- layerm->region()->config.solid_infill_extruder :
- layerm->region()->config.infill_extruder,
- 1)]);
- }
- }
- }
- if (ctxt.has_support) {
- const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
- if (support_layer) {
- for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
- extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(
- (extrusion_entity->role() == erSupportMaterial) ?
- support_layer->object()->config.support_material_extruder :
- support_layer->object()->config.support_material_interface_extruder,
- 2)]);
- }
- }
- }
- for (size_t i = 0; i < vols.size(); ++ i) {
- GLVolume &vol = *vols[i];
- if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
- // Store the vertex arrays and restart their containers,
- vols[i] = new_volume(vol.color);
- GLVolume &vol_new = *vols[i];
- // Assign the large pre-allocated buffers to the new GLVolume.
- vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
- // Copy the content back to the old GLVolume.
- vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
- vol_new.indexed_vertex_array.clear();
- // Just make sure that clear did not clear the reserved memory.
- vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- }
- }
- }
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
- });
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
- // Remove empty volumes from the newly added volumes.
- volumes->volumes.erase(
- std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(),
- [](const GLVolume *volume) { return volume->empty(); }),
- volumes->volumes.end());
- for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i)
- volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs);
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
-}
-
-void _3DScene::_load_wipe_tower_toolpaths(
- const Print *print,
- GLVolumeCollection *volumes,
- const std::vector<std::string> &tool_colors_str,
- bool use_VBOs)
-{
- if (print->m_wipe_tower_tool_changes.empty())
- return;
-
- std::vector<float> tool_colors = parse_colors(tool_colors_str);
-
- struct Ctxt
- {
- const Print *print;
- const std::vector<float> *tool_colors;
-
- // Number of vertices (each vertex is 6x4=24 bytes long)
- static const size_t alloc_size_max () { return 131072; } // 3.15MB
- static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
-
- static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
-
- // For cloring by a tool, return a parsed color.
- bool color_by_tool() const { return tool_colors != nullptr; }
- size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
- const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
- int volume_idx(int tool, int feature) const
- { return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature; }
-
- const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
- return priming.empty() ?
- ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
- ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
- }
- std::vector<WipeTower::ToolChangeResult> priming;
- std::vector<WipeTower::ToolChangeResult> final;
- } ctxt;
-
- ctxt.print = print;
- ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
- if (print->m_wipe_tower_priming)
- ctxt.priming.emplace_back(*print->m_wipe_tower_priming.get());
- if (print->m_wipe_tower_final_purge)
- ctxt.final.emplace_back(*print->m_wipe_tower_final_purge.get());
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
-
- //FIXME Improve the heuristics for a grain size.
- size_t n_items = print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
- size_t grain_size = std::max(n_items / 128, size_t(1));
- tbb::spin_mutex new_volume_mutex;
- auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* {
- auto *volume = new GLVolume(color);
- new_volume_mutex.lock();
- volume->outside_printer_detection_enabled = false;
- volumes->volumes.emplace_back(volume);
- new_volume_mutex.unlock();
- return volume;
- };
- const size_t volumes_cnt_initial = volumes->volumes.size();
- std::vector<GLVolumeCollection> volumes_per_thread(n_items);
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, n_items, grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
- // Bounding box of this slab of a wipe tower.
- std::vector<GLVolume*> vols;
- if (ctxt.color_by_tool()) {
- for (size_t i = 0; i < ctxt.number_tools(); ++ i)
- vols.emplace_back(new_volume(ctxt.color_tool(i)));
- } else
- vols = { new_volume(ctxt.color_support()) };
- for (GLVolume *volume : vols)
- volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
- const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
- for (size_t i = 0; i < vols.size(); ++ i) {
- GLVolume &vol = *vols[i];
- if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
- vol.print_zs.push_back(layer.front().print_z);
- vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
- vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
- }
- }
- for (const WipeTower::ToolChangeResult &extrusions : layer) {
- for (size_t i = 1; i < extrusions.extrusions.size();) {
- const WipeTower::Extrusion &e = extrusions.extrusions[i];
- if (e.width == 0.) {
- ++ i;
- continue;
- }
- size_t j = i + 1;
- if (ctxt.color_by_tool())
- for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++ j) ;
- else
- for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++ j) ;
- size_t n_lines = j - i;
- Lines lines;
- std::vector<double> widths;
- std::vector<double> heights;
- lines.reserve(n_lines);
- widths.reserve(n_lines);
- heights.assign(n_lines, extrusions.layer_height);
- for (; i < j; ++ i) {
- const WipeTower::Extrusion &e = extrusions.extrusions[i];
- assert(e.width > 0.f);
- const WipeTower::Extrusion &e_prev = *(&e - 1);
- lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
- widths.emplace_back(e.width);
- }
- thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
- *vols[ctxt.volume_idx(e.tool, 0)]);
- }
- }
- }
- for (size_t i = 0; i < vols.size(); ++ i) {
- GLVolume &vol = *vols[i];
- if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
- // Store the vertex arrays and restart their containers,
- vols[i] = new_volume(vol.color);
- GLVolume &vol_new = *vols[i];
- // Assign the large pre-allocated buffers to the new GLVolume.
- vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
- // Copy the content back to the old GLVolume.
- vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
- vol_new.indexed_vertex_array.clear();
- // Just make sure that clear did not clear the reserved memory.
- vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- }
- }
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
- });
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
- // Remove empty volumes from the newly added volumes.
- volumes->volumes.erase(
- std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(),
- [](const GLVolume *volume) { return volume->empty(); }),
- volumes->volumes.end());
- for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i)
- volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs);
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
+ return s_canvas_mgr.move_volume_up(canvas, id);
}
-void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors, bool use_VBOs)
+bool _3DScene::move_volume_down(wxGLCanvas* canvas, unsigned int id)
{
- // helper functions to select data in dependence of the extrusion view type
- struct Helper
- {
- static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path)
- {
- switch (type)
- {
- case GCodePreviewData::Extrusion::FeatureType:
- return (float)path.role();
- case GCodePreviewData::Extrusion::Height:
- return path.height;
- case GCodePreviewData::Extrusion::Width:
- return path.width;
- case GCodePreviewData::Extrusion::Feedrate:
- return path.feedrate;
- case GCodePreviewData::Extrusion::VolumetricRate:
- return path.feedrate * (float)path.mm3_per_mm;
- case GCodePreviewData::Extrusion::Tool:
- return (float)path.extruder_id;
- }
-
- return 0.0f;
- }
-
- static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
- {
- switch (data.extrusion.view_type)
- {
- case GCodePreviewData::Extrusion::FeatureType:
- return data.get_extrusion_role_color((ExtrusionRole)(int)value);
- case GCodePreviewData::Extrusion::Height:
- return data.get_height_color(value);
- case GCodePreviewData::Extrusion::Width:
- return data.get_width_color(value);
- case GCodePreviewData::Extrusion::Feedrate:
- return data.get_feedrate_color(value);
- case GCodePreviewData::Extrusion::VolumetricRate:
- return data.get_volumetric_rate_color(value);
- case GCodePreviewData::Extrusion::Tool:
- {
- GCodePreviewData::Color color;
- ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
- return color;
- }
- }
-
- return GCodePreviewData::Color::Dummy;
- }
- };
-
- // Helper structure for filters
- struct Filter
- {
- float value;
- ExtrusionRole role;
- GLVolume* volume;
-
- Filter(float value, ExtrusionRole role)
- : value(value)
- , role(role)
- , volume(nullptr)
- {
- }
-
- bool operator == (const Filter& other) const
- {
- if (value != other.value)
- return false;
-
- if (role != other.role)
- return false;
-
- return true;
- }
- };
+ return s_canvas_mgr.move_volume_down(canvas, id);
+}
- typedef std::vector<Filter> FiltersList;
- size_t initial_volumes_count = volumes.volumes.size();
+void _3DScene::set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections)
+{
+ s_canvas_mgr.set_objects_selections(canvas, selections);
+}
- // detects filters
- FiltersList filters;
- for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
- {
- for (const ExtrusionPath& path : layer.paths)
- {
- ExtrusionRole role = path.role();
- float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path);
- if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end())
- filters.emplace_back(path_filter, role);
- }
- }
+void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config)
+{
+ s_canvas_mgr.set_config(canvas, config);
+}
- // nothing to render, return
- if (filters.empty())
- return;
+void _3DScene::set_print(wxGLCanvas* canvas, Print* print)
+{
+ s_canvas_mgr.set_print(canvas, print);
+}
- // creates a new volume for each filter
- for (Filter& filter : filters)
- {
- s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)volumes.volumes.size());
- GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba);
- if (volume != nullptr)
- {
- filter.volume = volume;
- volumes.volumes.emplace_back(volume);
- }
- else
- {
- // an error occourred - restore to previous state and return
- s_gcode_preview_volume_index.first_volumes.pop_back();
- if (initial_volumes_count != volumes.volumes.size())
- {
- std::vector<GLVolume*>::iterator begin = volumes.volumes.begin() + initial_volumes_count;
- std::vector<GLVolume*>::iterator end = volumes.volumes.end();
- for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it)
- {
- GLVolume* volume = *it;
- delete volume;
- }
- volumes.volumes.erase(begin, end);
- return;
- }
- }
- }
+void _3DScene::set_model(wxGLCanvas* canvas, Model* model)
+{
+ s_canvas_mgr.set_model(canvas, model);
+}
- // populates volumes
- for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
- {
- for (const ExtrusionPath& path : layer.paths)
- {
- float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path);
- FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role()));
- if (filter != filters.end())
- {
- filter->volume->print_zs.push_back(layer.z);
- filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size());
- filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size());
+void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape)
+{
+ return s_canvas_mgr.set_bed_shape(canvas, shape);
+}
- extrusionentity_to_verts(path, layer.z, *filter->volume);
- }
- }
- }
+void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas)
+{
+ return s_canvas_mgr.set_auto_bed_shape(canvas);
+}
- // finalize volumes and sends geometry to gpu
- if (volumes.volumes.size() > initial_volumes_count)
- {
- for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i)
- {
- GLVolume* volume = volumes.volumes[i];
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(use_VBOs);
- }
- }
+BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas)
+{
+ return s_canvas_mgr.get_volumes_bounding_box(canvas);
}
-void _3DScene::_load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors, bool use_VBOs)
+void _3DScene::set_axes_length(wxGLCanvas* canvas, float length)
{
- size_t initial_volumes_count = volumes.volumes.size();
- s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count);
+ s_canvas_mgr.set_axes_length(canvas, length);
+}
- bool res = true;
- switch (preview_data.extrusion.view_type)
- {
- case GCodePreviewData::Extrusion::Feedrate:
- {
- res = _travel_paths_by_feedrate(preview_data, volumes);
- break;
- }
- case GCodePreviewData::Extrusion::Tool:
- {
- res = _travel_paths_by_tool(preview_data, volumes, tool_colors);
- break;
- }
- default:
- {
- res = _travel_paths_by_type(preview_data, volumes);
- break;
- }
- }
+void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons)
+{
+ return s_canvas_mgr.set_cutting_plane(canvas, z, polygons);
+}
- if (!res)
- {
- // an error occourred - restore to previous state and return
- if (initial_volumes_count != volumes.volumes.size())
- {
- std::vector<GLVolume*>::iterator begin = volumes.volumes.begin() + initial_volumes_count;
- std::vector<GLVolume*>::iterator end = volumes.volumes.end();
- for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it)
- {
- GLVolume* volume = *it;
- delete volume;
- }
- volumes.volumes.erase(begin, end);
- }
+void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value)
+{
+ return s_canvas_mgr.set_color_by(canvas, value);
+}
- return;
- }
+void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value)
+{
+ return s_canvas_mgr.set_select_by(canvas, value);
+}
- // finalize volumes and sends geometry to gpu
- if (volumes.volumes.size() > initial_volumes_count)
- {
- for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i)
- {
- GLVolume* volume = volumes.volumes[i];
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(use_VBOs);
- }
- }
+void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value)
+{
+ return s_canvas_mgr.set_drag_by(canvas, value);
}
-bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes)
+bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas)
{
- // Helper structure for types
- struct Type
- {
- GCodePreviewData::Travel::EType value;
- GLVolume* volume;
+ return s_canvas_mgr.is_layers_editing_enabled(canvas);
+}
- explicit Type(GCodePreviewData::Travel::EType value)
- : value(value)
- , volume(nullptr)
- {
- }
+bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas)
+{
+ return s_canvas_mgr.is_layers_editing_allowed(canvas);
+}
- bool operator == (const Type& other) const
- {
- return value == other.value;
- }
- };
+bool _3DScene::is_shader_enabled(wxGLCanvas* canvas)
+{
+ return s_canvas_mgr.is_shader_enabled(canvas);
+}
- typedef std::vector<Type> TypesList;
+bool _3DScene::is_reload_delayed(wxGLCanvas* canvas)
+{
+ return s_canvas_mgr.is_reload_delayed(canvas);
+}
- // colors travels by travel type
+void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_layers_editing(canvas, enable);
+}
- // detects types
- TypesList types;
- for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
- {
- if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end())
- types.emplace_back(polyline.type);
- }
+void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_warning_texture(canvas, enable);
+}
- // nothing to render, return
- if (types.empty())
- return true;
+void _3DScene::enable_legend_texture(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_legend_texture(canvas, enable);
+}
- // creates a new volume for each type
- for (Type& type : types)
- {
- GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba);
- if (volume == nullptr)
- return false;
- else
- {
- type.volume = volume;
- volumes.volumes.emplace_back(volume);
- }
- }
+void _3DScene::enable_picking(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_picking(canvas, enable);
+}
- // populates volumes
- for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
- {
- TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type));
- if (type != types.end())
- {
- type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
- type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size());
- type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size());
+void _3DScene::enable_moving(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_moving(canvas, enable);
+}
- polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume);
- }
- }
+void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_gizmos(canvas, enable);
+}
- return true;
+void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_shader(canvas, enable);
}
-bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes)
+void _3DScene::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable)
{
- // Helper structure for feedrate
- struct Feedrate
- {
- float value;
- GLVolume* volume;
+ s_canvas_mgr.enable_force_zoom_to_bed(canvas, enable);
+}
- explicit Feedrate(float value)
- : value(value)
- , volume(nullptr)
- {
- }
+void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow)
+{
+ s_canvas_mgr.allow_multisample(canvas, allow);
+}
- bool operator == (const Feedrate& other) const
- {
- return value == other.value;
- }
- };
+void _3DScene::zoom_to_bed(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.zoom_to_bed(canvas);
+}
- typedef std::vector<Feedrate> FeedratesList;
+void _3DScene::zoom_to_volumes(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.zoom_to_volumes(canvas);
+}
- // colors travels by feedrate
+void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction)
+{
+ s_canvas_mgr.select_view(canvas, direction);
+}
- // detects feedrates
- FeedratesList feedrates;
- for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
- {
- if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end())
- feedrates.emplace_back(polyline.feedrate);
- }
+void _3DScene::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other)
+{
+ s_canvas_mgr.set_viewport_from_scene(canvas, other);
+}
- // nothing to render, return
- if (feedrates.empty())
- return true;
+void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.update_volumes_colors_by_extruder(canvas);
+}
- // creates a new volume for each feedrate
- for (Feedrate& feedrate : feedrates)
- {
- GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba);
- if (volume == nullptr)
- return false;
- else
- {
- feedrate.volume = volume;
- volumes.volumes.emplace_back(volume);
- }
- }
+void _3DScene::render(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.render(canvas);
+}
- // populates volumes
- for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
- {
- FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate));
- if (feedrate != feedrates.end())
- {
- feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
- feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size());
- feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size());
+std::vector<double> _3DScene::get_current_print_zs(wxGLCanvas* canvas, bool active_only)
+{
+ return s_canvas_mgr.get_current_print_zs(canvas, active_only);
+}
- polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume);
- }
- }
+void _3DScene::set_toolpaths_range(wxGLCanvas* canvas, double low, double high)
+{
+ s_canvas_mgr.set_toolpaths_range(canvas, low, high);
+}
- return true;
+void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback);
}
-bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors)
+void _3DScene::register_on_double_click_callback(wxGLCanvas* canvas, void* callback)
{
- // Helper structure for tool
- struct Tool
- {
- unsigned int value;
- GLVolume* volume;
+ s_canvas_mgr.register_on_double_click_callback(canvas, callback);
+}
- explicit Tool(unsigned int value)
- : value(value)
- , volume(nullptr)
- {
- }
+void _3DScene::register_on_right_click_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_right_click_callback(canvas, callback);
+}
- bool operator == (const Tool& other) const
- {
- return value == other.value;
- }
- };
+void _3DScene::register_on_select_object_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_select_object_callback(canvas, callback);
+}
- typedef std::vector<Tool> ToolsList;
+void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_model_update_callback(canvas, callback);
+}
- // colors travels by tool
+void _3DScene::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_remove_object_callback(canvas, callback);
+}
- // detects tools
- ToolsList tools;
- for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
- {
- if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end())
- tools.emplace_back(polyline.extruder_id);
- }
+void _3DScene::register_on_arrange_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_arrange_callback(canvas, callback);
+}
- // nothing to render, return
- if (tools.empty())
- return true;
+void _3DScene::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_rotate_object_left_callback(canvas, callback);
+}
- // creates a new volume for each tool
- for (Tool& tool : tools)
- {
- GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4);
- if (volume == nullptr)
- return false;
- else
- {
- tool.volume = volume;
- volumes.volumes.emplace_back(volume);
- }
- }
+void _3DScene::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_rotate_object_right_callback(canvas, callback);
+}
- // populates volumes
- for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
- {
- ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
- if (tool != tools.end())
- {
- tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
- tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size());
- tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size());
+void _3DScene::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_scale_object_uniformly_callback(canvas, callback);
+}
- polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume);
- }
- }
+void _3DScene::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_increase_objects_callback(canvas, callback);
+}
- return true;
+void _3DScene::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_decrease_objects_callback(canvas, callback);
}
-void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs)
+void _3DScene::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback)
{
- s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)volumes.volumes.size());
+ s_canvas_mgr.register_on_instance_moved_callback(canvas, callback);
+}
- // nothing to render, return
- if (preview_data.retraction.positions.empty())
- return;
+void _3DScene::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_wipe_tower_moved_callback(canvas, callback);
+}
- GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba);
- if (volume != nullptr)
- {
- volumes.volumes.emplace_back(volume);
+void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback);
+}
- GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions);
- std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
+void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback);
+}
- for (const GCodePreviewData::Retraction::Position& position : copy)
- {
- volume->print_zs.push_back(unscale(position.position.z));
- volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
- volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
+static inline int hex_digit_to_int(const char c)
+{
+ return
+ (c >= '0' && c <= '9') ? int(c - '0') :
+ (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
+ (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
+}
- point3_to_verts(position.position, position.width, position.height, *volume);
+static inline std::vector<float> parse_colors(const std::vector<std::string> &scolors)
+{
+ std::vector<float> output(scolors.size() * 4, 1.f);
+ for (size_t i = 0; i < scolors.size(); ++ i) {
+ const std::string &scolor = scolors[i];
+ const char *c = scolor.data() + 1;
+ if (scolor.size() == 7 && scolor.front() == '#') {
+ for (size_t j = 0; j < 3; ++j) {
+ int digit1 = hex_digit_to_int(*c ++);
+ int digit2 = hex_digit_to_int(*c ++);
+ if (digit1 == -1 || digit2 == -1)
+ break;
+ output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.f;
+ }
}
-
- // finalize volumes and sends geometry to gpu
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(use_VBOs);
}
+ return output;
}
-void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs)
+std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs)
{
- s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)volumes.volumes.size());
-
- // nothing to render, return
- if (preview_data.unretraction.positions.empty())
- return;
+ return s_canvas_mgr.load_object(canvas, model_object, obj_idx, instance_idxs);
+}
- GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba);
- if (volume != nullptr)
- {
- volumes.volumes.emplace_back(volume);
+std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx)
+{
+ return s_canvas_mgr.load_object(canvas, model, obj_idx);
+}
- GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions);
- std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
+void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
+{
+ s_canvas_mgr.reload_scene(canvas, force);
+}
- for (const GCodePreviewData::Retraction::Position& position : copy)
- {
- volume->print_zs.push_back(unscale(position.position.z));
- volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
- volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
+void _3DScene::load_print_toolpaths(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.load_print_toolpaths(canvas);
+}
- point3_to_verts(position.position, position.width, position.height, *volume);
- }
+void _3DScene::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors)
+{
+ s_canvas_mgr.load_print_object_toolpaths(canvas, print_object, str_tool_colors);
+}
- // finalize volumes and sends geometry to gpu
- volume->bounding_box = volume->indexed_vertex_array.bounding_box();
- volume->indexed_vertex_array.finalize_geometry(use_VBOs);
- }
+void _3DScene::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
+{
+ s_canvas_mgr.load_wipe_tower_toolpaths(canvas, str_tool_colors);
}
-void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes)
+void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
- unsigned int size = (unsigned int)s_gcode_preview_volume_index.first_volumes.size();
- for (unsigned int i = 0; i < size; ++i)
- {
- std::vector<GLVolume*>::iterator begin = volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i].id;
- std::vector<GLVolume*>::iterator end = (i + 1 < size) ? volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i + 1].id : volumes.volumes.end();
+ s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
+}
- for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
- {
- GLVolume* volume = *it;
- volume->outside_printer_detection_enabled = false;
+void _3DScene::generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+{
+ s_legend_texture.generate(preview_data, tool_colors);
+}
- switch (s_gcode_preview_volume_index.first_volumes[i].type)
- {
- case GCodePreviewVolumeIndex::Extrusion:
- {
- if ((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag == erCustom)
- volume->zoom_to_volumes = false;
-
- volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag);
- break;
- }
- case GCodePreviewVolumeIndex::Travel:
- {
- volume->is_active = preview_data.travel.is_visible;
- volume->zoom_to_volumes = false;
- break;
- }
- case GCodePreviewVolumeIndex::Retraction:
- {
- volume->is_active = preview_data.retraction.is_visible;
- volume->zoom_to_volumes = false;
- break;
- }
- case GCodePreviewVolumeIndex::Unretraction:
- {
- volume->is_active = preview_data.unretraction.is_visible;
- volume->zoom_to_volumes = false;
- break;
- }
- case GCodePreviewVolumeIndex::Shell:
- {
- volume->is_active = preview_data.shell.is_visible;
- volume->color[3] = 0.25f;
- volume->zoom_to_volumes = false;
- break;
- }
- default:
- {
- volume->is_active = false;
- volume->zoom_to_volumes = false;
- break;
- }
- }
- }
- }
+unsigned int _3DScene::get_legend_texture_width()
+{
+ return s_legend_texture.get_texture_width();
}
-void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+unsigned int _3DScene::get_legend_texture_height()
{
- s_legend_texture.generate(preview_data, tool_colors);
+ return s_legend_texture.get_texture_height();
}
-void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs)
+void _3DScene::reset_legend_texture()
{
- size_t initial_volumes_count = volumes.volumes.size();
- s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count);
+ s_legend_texture.reset_texture();
+}
- if (print.objects.empty())
- // nothing to render, return
- return;
+unsigned int _3DScene::finalize_legend_texture()
+{
+ return s_legend_texture.finalize();
+}
- // adds objects' volumes
- unsigned int object_id = 0;
- for (PrintObject* obj : print.objects)
- {
- ModelObject* model_obj = obj->model_object();
+unsigned int _3DScene::get_warning_texture_width()
+{
+ return s_warning_texture.get_texture_width();
+}
- std::vector<int> instance_ids(model_obj->instances.size());
- for (int i = 0; i < model_obj->instances.size(); ++i)
- {
- instance_ids[i] = i;
- }
+unsigned int _3DScene::get_warning_texture_height()
+{
+ return s_warning_texture.get_texture_height();
+}
- for (ModelInstance* instance : model_obj->instances)
- {
- volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", use_VBOs);
- }
+void _3DScene::generate_warning_texture(const std::string& msg)
+{
+ s_warning_texture.generate(msg);
+}
- ++object_id;
- }
+void _3DScene::reset_warning_texture()
+{
+ s_warning_texture.reset_texture();
+}
- // adds wipe tower's volume
- coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z;
- const PrintConfig& config = print.config;
- unsigned int extruders_count = config.nozzle_diameter.size();
- if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
- const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
- volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs);
- }
+unsigned int _3DScene::finalize_warning_texture()
+{
+ return s_warning_texture.finalize();
}
} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index a417f5f9d..c6a166397 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -6,8 +6,10 @@
#include "../../libslic3r/Line.hpp"
#include "../../libslic3r/TriangleMesh.hpp"
#include "../../libslic3r/Utils.hpp"
+#include "../../slic3r/GUI/GLCanvas3DManager.hpp"
class wxBitmap;
+class wxWindow;
namespace Slic3r {
@@ -17,6 +19,11 @@ class Model;
class ModelObject;
class GCodePreviewData;
class DynamicPrintConfig;
+class ExtrusionPath;
+class ExtrusionMultiPath;
+class ExtrusionLoop;
+class ExtrusionEntity;
+class ExtrusionEntityCollection;
// A container for interleaved arrays of 3D vertices and normals,
// possibly indexed by triangles and / or quads.
@@ -305,9 +312,9 @@ public:
// Boolean: Is mouse over this object?
bool hover;
// Wheter or not this volume has been generated from a modifier
- bool is_modifier;
+ bool is_modifier;
// Wheter or not this volume has been generated from the wipe tower
- bool is_wipe_tower;
+ bool is_wipe_tower;
// Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array;
@@ -437,35 +444,6 @@ private:
class _3DScene
{
- struct GCodePreviewVolumeIndex
- {
- enum EType
- {
- Extrusion,
- Travel,
- Retraction,
- Unretraction,
- Shell,
- Num_Geometry_Types
- };
-
- struct FirstVolume
- {
- EType type;
- unsigned int flag;
- // Index of the first volume in a GLVolumeCollection.
- unsigned int id;
-
- FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {}
- };
-
- std::vector<FirstVolume> first_volumes;
-
- void reset() { first_volumes.clear(); }
- };
-
- static GCodePreviewVolumeIndex s_gcode_preview_volume_index;
-
class TextureBase
{
protected:
@@ -525,12 +503,106 @@ class _3DScene
static LegendTexture s_legend_texture;
static WarningTexture s_warning_texture;
+ static GUI::GLCanvas3DManager s_canvas_mgr;
public:
- static void _glew_init();
+ static void init_gl();
+ static std::string get_gl_info(bool format_as_html, bool extensions);
+ static bool use_VBOs();
+
+ static bool add_canvas(wxGLCanvas* canvas);
+ static bool remove_canvas(wxGLCanvas* canvas);
+ static void remove_all_canvases();
+
+ static bool init(wxGLCanvas* canvas);
+
+ static void set_active(wxGLCanvas* canvas, bool active);
+
+ static unsigned int get_volumes_count(wxGLCanvas* canvas);
+ static void reset_volumes(wxGLCanvas* canvas);
+ static void deselect_volumes(wxGLCanvas* canvas);
+ static void select_volume(wxGLCanvas* canvas, unsigned int id);
+ static void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
+ static bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
+ static bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
+ static bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
+
+ static void set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections);
+
+ static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config);
+ static void set_print(wxGLCanvas* canvas, Print* print);
+ static void set_model(wxGLCanvas* canvas, Model* model);
+
+ static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape);
+ static void set_auto_bed_shape(wxGLCanvas* canvas);
+
+ static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas);
+
+ static void set_axes_length(wxGLCanvas* canvas, float length);
- static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector<std::string>& str_tool_colors, bool use_VBOs);
+ static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons);
+ static void set_color_by(wxGLCanvas* canvas, const std::string& value);
+ static void set_select_by(wxGLCanvas* canvas, const std::string& value);
+ static void set_drag_by(wxGLCanvas* canvas, const std::string& value);
+
+ static bool is_layers_editing_enabled(wxGLCanvas* canvas);
+ static bool is_layers_editing_allowed(wxGLCanvas* canvas);
+ static bool is_shader_enabled(wxGLCanvas* canvas);
+
+ static bool is_reload_delayed(wxGLCanvas* canvas);
+
+ static void enable_layers_editing(wxGLCanvas* canvas, bool enable);
+ static void enable_warning_texture(wxGLCanvas* canvas, bool enable);
+ static void enable_legend_texture(wxGLCanvas* canvas, bool enable);
+ static void enable_picking(wxGLCanvas* canvas, bool enable);
+ static void enable_moving(wxGLCanvas* canvas, bool enable);
+ static void enable_gizmos(wxGLCanvas* canvas, bool enable);
+ static void enable_shader(wxGLCanvas* canvas, bool enable);
+ static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
+ static void allow_multisample(wxGLCanvas* canvas, bool allow);
+
+ static void zoom_to_bed(wxGLCanvas* canvas);
+ static void zoom_to_volumes(wxGLCanvas* canvas);
+ static void select_view(wxGLCanvas* canvas, const std::string& direction);
+ static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
+
+ static void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
+
+ static void render(wxGLCanvas* canvas);
+
+ static std::vector<double> get_current_print_zs(wxGLCanvas* canvas, bool active_only);
+ static void set_toolpaths_range(wxGLCanvas* canvas, double low, double high);
+
+ static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_select_object_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_arrange_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
+
+ static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
+ static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
+
+ static void reload_scene(wxGLCanvas* canvas, bool force);
+
+ static void load_print_toolpaths(wxGLCanvas* canvas);
+ static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors);
+ static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
+ static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
+
+ // generates the legend texture in dependence of the current shown view type
+ static void generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
static unsigned int get_legend_texture_width();
static unsigned int get_legend_texture_height();
@@ -545,42 +617,16 @@ public:
static void reset_warning_texture();
static unsigned int finalize_warning_texture();
- static void _load_print_toolpaths(
- const Print *print,
- GLVolumeCollection *volumes,
- const std::vector<std::string> &tool_colors,
- bool use_VBOs);
-
- static void _load_print_object_toolpaths(
- const PrintObject *print_object,
- GLVolumeCollection *volumes,
- const std::vector<std::string> &tool_colors,
- bool use_VBOs);
-
- static void _load_wipe_tower_toolpaths(
- const Print *print,
- GLVolumeCollection *volumes,
- const std::vector<std::string> &tool_colors_str,
- bool use_VBOs);
-
-private:
- // generates gcode extrusion paths geometry
- static void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors, bool use_VBOs);
- // generates gcode travel paths geometry
- static void _load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors, bool use_VBOs);
- static bool _travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes);
- static bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes);
- static bool _travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors);
- // generates gcode retractions geometry
- static void _load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs);
- // generates gcode unretractions geometry
- static void _load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs);
- // sets gcode geometry visibility according to user selection
- static void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes);
- // generates the legend texture in dependence of the current shown view type
- static void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
- // generates objects and wipe tower geometry
- static void _load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs);
+ static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
+ static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
+ static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume);
+ static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume);
+ static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume);
+ static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume);
+ static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume);
+ static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume);
+ static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume);
+ static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume);
};
}
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
new file mode 100644
index 000000000..f9c10017e
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -0,0 +1,4308 @@
+#include "GLCanvas3D.hpp"
+
+#include "../../slic3r/GUI/3DScene.hpp"
+#include "../../slic3r/GUI/GLShader.hpp"
+#include "../../slic3r/GUI/GUI.hpp"
+#include "../../slic3r/GUI/PresetBundle.hpp"
+#include "../../slic3r/GUI/GLGizmo.hpp"
+#include "../../libslic3r/ClipperUtils.hpp"
+#include "../../libslic3r/PrintConfig.hpp"
+#include "../../libslic3r/Print.hpp"
+#include "../../libslic3r/GCode/PreviewData.hpp"
+
+#include <GL/glew.h>
+
+#include <wx/glcanvas.h>
+#include <wx/timer.h>
+
+#include <tbb/parallel_for.h>
+#include <tbb/spin_mutex.h>
+
+#include <boost/log/trivial.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <iostream>
+#include <float.h>
+#include <algorithm>
+
+static const float TRACKBALLSIZE = 0.8f;
+static const float GIMBALL_LOCK_THETA_MAX = 180.0f;
+static const float GROUND_Z = -0.02f;
+
+// phi / theta angles to orient the camera.
+static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f };
+static const float VIEW_LEFT[2] = { 90.0f, 90.0f };
+static const float VIEW_RIGHT[2] = { -90.0f, 90.0f };
+static const float VIEW_TOP[2] = { 0.0f, 0.0f };
+static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f };
+static const float VIEW_FRONT[2] = { 0.0f, 90.0f };
+static const float VIEW_REAR[2] = { 180.0f, 90.0f };
+
+static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f;
+static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
+
+namespace Slic3r {
+namespace GUI {
+
+bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords)
+{
+ m_vertices.clear();
+ m_tex_coords.clear();
+
+ unsigned int v_size = 9 * (unsigned int)triangles.size();
+ unsigned int t_size = 6 * (unsigned int)triangles.size();
+ if (v_size == 0)
+ return false;
+
+ m_vertices = std::vector<float>(v_size, 0.0f);
+ if (generate_tex_coords)
+ m_tex_coords = std::vector<float>(t_size, 0.0f);
+
+ float min_x = (float)unscale(triangles[0].points[0].x);
+ float min_y = (float)unscale(triangles[0].points[0].y);
+ float max_x = min_x;
+ float max_y = min_y;
+
+ unsigned int v_coord = 0;
+ unsigned int t_coord = 0;
+ for (const Polygon& t : triangles)
+ {
+ for (unsigned int v = 0; v < 3; ++v)
+ {
+ const Point& p = t.points[v];
+ float x = (float)unscale(p.x);
+ float y = (float)unscale(p.y);
+
+ m_vertices[v_coord++] = x;
+ m_vertices[v_coord++] = y;
+ m_vertices[v_coord++] = z;
+
+ if (generate_tex_coords)
+ {
+ m_tex_coords[t_coord++] = x;
+ m_tex_coords[t_coord++] = y;
+
+ min_x = std::min(min_x, x);
+ max_x = std::max(max_x, x);
+ min_y = std::min(min_y, y);
+ max_y = std::max(max_y, y);
+ }
+ }
+ }
+
+ if (generate_tex_coords)
+ {
+ float size_x = max_x - min_x;
+ float size_y = max_y - min_y;
+
+ if ((size_x != 0.0f) && (size_y != 0.0f))
+ {
+ float inv_size_x = 1.0f / size_x;
+ float inv_size_y = -1.0f / size_y;
+ for (unsigned int i = 0; i < m_tex_coords.size(); i += 2)
+ {
+ m_tex_coords[i] *= inv_size_x;
+ m_tex_coords[i + 1] *= inv_size_y;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
+{
+ m_vertices.clear();
+ m_tex_coords.clear();
+
+ unsigned int size = 6 * (unsigned int)lines.size();
+ if (size == 0)
+ return false;
+
+ m_vertices = std::vector<float>(size, 0.0f);
+
+ unsigned int coord = 0;
+ for (const Line& l : lines)
+ {
+ m_vertices[coord++] = (float)unscale(l.a.x);
+ m_vertices[coord++] = (float)unscale(l.a.y);
+ m_vertices[coord++] = z;
+ m_vertices[coord++] = (float)unscale(l.b.x);
+ m_vertices[coord++] = (float)unscale(l.b.y);
+ m_vertices[coord++] = z;
+ }
+
+ return true;
+}
+
+const float* GeometryBuffer::get_vertices() const
+{
+ return m_vertices.data();
+}
+
+const float* GeometryBuffer::get_tex_coords() const
+{
+ return m_tex_coords.data();
+}
+
+unsigned int GeometryBuffer::get_vertices_count() const
+{
+ return (unsigned int)m_vertices.size() / 3;
+}
+
+Size::Size()
+ : m_width(0)
+ , m_height(0)
+{
+}
+
+Size::Size(int width, int height)
+ : m_width(width)
+ , m_height(height)
+{
+}
+
+int Size::get_width() const
+{
+ return m_width;
+}
+
+void Size::set_width(int width)
+{
+ m_width = width;
+}
+
+int Size::get_height() const
+{
+ return m_height;
+}
+
+void Size::set_height(int height)
+{
+ m_height = height;
+}
+
+Rect::Rect()
+ : m_left(0.0f)
+ , m_top(0.0f)
+ , m_right(0.0f)
+ , m_bottom(0.0f)
+{
+}
+
+Rect::Rect(float left, float top, float right, float bottom)
+ : m_left(left)
+ , m_top(top)
+ , m_right(right)
+ , m_bottom(bottom)
+{
+}
+
+float Rect::get_left() const
+{
+ return m_left;
+}
+
+void Rect::set_left(float left)
+{
+ m_left = left;
+}
+
+float Rect::get_top() const
+{
+ return m_top;
+}
+
+void Rect::set_top(float top)
+{
+ m_top = top;
+}
+
+float Rect::get_right() const
+{
+ return m_right;
+}
+
+void Rect::set_right(float right)
+{
+ m_right = right;
+}
+
+float Rect::get_bottom() const
+{
+ return m_bottom;
+}
+
+void Rect::set_bottom(float bottom)
+{
+ m_bottom = bottom;
+}
+
+GLCanvas3D::Camera::Camera()
+ : type(Ortho)
+ , zoom(1.0f)
+ , phi(45.0f)
+// , distance(0.0f)
+ , target(0.0, 0.0, 0.0)
+ , m_theta(45.0f)
+{
+}
+
+std::string GLCanvas3D::Camera::get_type_as_string() const
+{
+ switch (type)
+ {
+ default:
+ case Unknown:
+ return "unknown";
+// case Perspective:
+// return "perspective";
+ case Ortho:
+ return "ortho";
+ };
+}
+
+float GLCanvas3D::Camera::get_theta() const
+{
+ return m_theta;
+}
+
+void GLCanvas3D::Camera::set_theta(float theta)
+{
+ m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta);
+}
+
+GLCanvas3D::Bed::Bed()
+ : m_type(Custom)
+{
+}
+
+bool GLCanvas3D::Bed::is_prusa() const
+{
+ return (m_type == MK2) || (m_type == MK3);
+}
+
+bool GLCanvas3D::Bed::is_custom() const
+{
+ return m_type == Custom;
+}
+
+const Pointfs& GLCanvas3D::Bed::get_shape() const
+{
+ return m_shape;
+}
+
+void GLCanvas3D::Bed::set_shape(const Pointfs& shape)
+{
+ m_shape = shape;
+ m_type = _detect_type();
+
+ _calc_bounding_box();
+
+ ExPolygon poly;
+ for (const Pointf& p : m_shape)
+ {
+ poly.contour.append(Point(scale_(p.x), scale_(p.y)));
+ }
+
+ _calc_triangles(poly);
+
+ const BoundingBox& bed_bbox = poly.contour.bounding_box();
+ _calc_gridlines(poly, bed_bbox);
+
+ m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour;
+}
+
+const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const
+{
+ return m_bounding_box;
+}
+
+bool GLCanvas3D::Bed::contains(const Point& point) const
+{
+ return m_polygon.contains(point);
+}
+
+Point GLCanvas3D::Bed::point_projection(const Point& point) const
+{
+ return m_polygon.point_projection(point);
+}
+
+void GLCanvas3D::Bed::render(float theta) const
+{
+ switch (m_type)
+ {
+ case MK2:
+ {
+ _render_mk2(theta);
+ break;
+ }
+ case MK3:
+ {
+ _render_mk3(theta);
+ break;
+ }
+ default:
+ case Custom:
+ {
+ _render_custom();
+ break;
+ }
+ }
+}
+
+void GLCanvas3D::Bed::_calc_bounding_box()
+{
+ m_bounding_box = BoundingBoxf3();
+ for (const Pointf& p : m_shape)
+ {
+ m_bounding_box.merge(Pointf3(p.x, p.y, 0.0));
+ }
+}
+
+void GLCanvas3D::Bed::_calc_triangles(const ExPolygon& poly)
+{
+ Polygons triangles;
+ poly.triangulate(&triangles);
+
+ if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom))
+ printf("Unable to create bed triangles\n");
+}
+
+void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
+{
+ Polylines axes_lines;
+ for (coord_t x = bed_bbox.min.x; x <= bed_bbox.max.x; x += scale_(10.0))
+ {
+ Polyline line;
+ line.append(Point(x, bed_bbox.min.y));
+ line.append(Point(x, bed_bbox.max.y));
+ axes_lines.push_back(line);
+ }
+ for (coord_t y = bed_bbox.min.y; y <= bed_bbox.max.y; y += scale_(10.0))
+ {
+ Polyline line;
+ line.append(Point(bed_bbox.min.x, y));
+ line.append(Point(bed_bbox.max.x, y));
+ axes_lines.push_back(line);
+ }
+
+ // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
+ Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, SCALED_EPSILON)));
+
+ // append bed contours
+ Lines contour_lines = to_lines(poly);
+ std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
+
+ if (!m_gridlines.set_from_lines(gridlines, GROUND_Z))
+ printf("Unable to create bed grid lines\n");
+}
+
+GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const
+{
+ EType type = Custom;
+
+ const PresetBundle* bundle = get_preset_bundle();
+ if (bundle != nullptr)
+ {
+ const Preset& curr = bundle->printers.get_selected_preset();
+ if (curr.config.has("bed_shape") && _are_equal(m_shape, dynamic_cast<const ConfigOptionPoints*>(curr.config.option("bed_shape"))->values))
+ {
+ if ((curr.vendor != nullptr) && (curr.vendor->name == "Prusa Research"))
+ {
+ if (boost::contains(curr.name, "MK2"))
+ type = MK2;
+ else if (boost::contains(curr.name, "MK3"))
+ type = MK3;
+ }
+ }
+ }
+
+ return type;
+}
+
+void GLCanvas3D::Bed::_render_mk2(float theta) const
+{
+ std::string filename = resources_dir() + "/icons/bed/mk2_top.png";
+ if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
+ {
+ if (!m_top_texture.load_from_file(filename, true))
+ {
+ _render_custom();
+ return;
+ }
+ }
+
+ filename = resources_dir() + "/icons/bed/mk2_bottom.png";
+ if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
+ {
+ if (!m_bottom_texture.load_from_file(filename, true))
+ {
+ _render_custom();
+ return;
+ }
+ }
+
+ _render_prusa(theta);
+}
+
+void GLCanvas3D::Bed::_render_mk3(float theta) const
+{
+ std::string filename = resources_dir() + "/icons/bed/mk3_top.png";
+ if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
+ {
+ if (!m_top_texture.load_from_file(filename, true))
+ {
+ _render_custom();
+ return;
+ }
+ }
+
+ filename = resources_dir() + "/icons/bed/mk3_bottom.png";
+ if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
+ {
+ if (!m_bottom_texture.load_from_file(filename, true))
+ {
+ _render_custom();
+ return;
+ }
+ }
+
+ _render_prusa(theta);
+}
+
+void GLCanvas3D::Bed::_render_prusa(float theta) const
+{
+ unsigned int triangles_vcount = m_triangles.get_vertices_count();
+ if (triangles_vcount > 0)
+ {
+ ::glEnable(GL_DEPTH_TEST);
+
+ ::glEnable(GL_BLEND);
+ ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ ::glEnable(GL_TEXTURE_2D);
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ if (theta > 90.0f)
+ ::glFrontFace(GL_CW);
+
+ ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ ::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id());
+ ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
+ ::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords());
+ ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount);
+
+ if (theta > 90.0f)
+ ::glFrontFace(GL_CCW);
+
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+ ::glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+
+ ::glDisable(GL_TEXTURE_2D);
+
+ ::glDisable(GL_BLEND);
+ }
+}
+
+void GLCanvas3D::Bed::_render_custom() const
+{
+ m_top_texture.reset();
+ m_bottom_texture.reset();
+
+ unsigned int triangles_vcount = m_triangles.get_vertices_count();
+ if (triangles_vcount > 0)
+ {
+ ::glEnable(GL_LIGHTING);
+ ::glDisable(GL_DEPTH_TEST);
+
+ ::glEnable(GL_BLEND);
+ ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+
+ ::glColor4f(0.8f, 0.6f, 0.5f, 0.4f);
+ ::glNormal3d(0.0f, 0.0f, 1.0f);
+ ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
+ ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount);
+
+ // draw grid
+ unsigned int gridlines_vcount = m_gridlines.get_vertices_count();
+
+ // we need depth test for grid, otherwise it would disappear when looking the object from below
+ ::glEnable(GL_DEPTH_TEST);
+ ::glLineWidth(3.0f);
+ ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f);
+ ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices());
+ ::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount);
+
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+
+ ::glDisable(GL_BLEND);
+ }
+}
+
+bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2)
+{
+ if (bed_1.size() != bed_2.size())
+ return false;
+
+ for (unsigned int i = 0; i < (unsigned int)bed_1.size(); ++i)
+ {
+ if (bed_1[i] != bed_2[i])
+ return false;
+ }
+
+ return true;
+}
+
+GLCanvas3D::Axes::Axes()
+ : length(0.0f)
+{
+}
+
+void GLCanvas3D::Axes::render(bool depth_test) const
+{
+ ::glDisable(GL_LIGHTING);
+ if (depth_test)
+ ::glEnable(GL_DEPTH_TEST);
+ else
+ ::glDisable(GL_DEPTH_TEST);
+
+ ::glLineWidth(2.0f);
+ ::glBegin(GL_LINES);
+ // draw line for x axis
+ ::glColor3f(1.0f, 0.0f, 0.0f);
+ ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z);
+ ::glVertex3f((GLfloat)origin.x + length, (GLfloat)origin.y, (GLfloat)origin.z);
+ // draw line for y axis
+ ::glColor3f(0.0f, 1.0f, 0.0f);
+ ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z);
+ ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y + length, (GLfloat)origin.z);
+ ::glEnd();
+ // draw line for Z axis
+ // (re-enable depth test so that axis is correctly shown when objects are behind it)
+ if (!depth_test)
+ ::glEnable(GL_DEPTH_TEST);
+
+ ::glBegin(GL_LINES);
+ ::glColor3f(0.0f, 0.0f, 1.0f);
+ ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z);
+ ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z + length);
+ ::glEnd();
+}
+
+GLCanvas3D::CuttingPlane::CuttingPlane()
+ : m_z(-1.0f)
+{
+}
+
+bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons)
+{
+ m_z = z;
+
+ // grow slices in order to display them better
+ ExPolygons expolygons = offset_ex(polygons, scale_(0.1));
+ Lines lines = to_lines(expolygons);
+ return m_lines.set_from_lines(lines, m_z);
+}
+
+void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const
+{
+ ::glDisable(GL_LIGHTING);
+ _render_plane(bb);
+ _render_contour();
+}
+
+void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) const
+{
+ if (m_z >= 0.0f)
+ {
+ ::glDisable(GL_CULL_FACE);
+ ::glEnable(GL_BLEND);
+ ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ float margin = 20.0f;
+ float min_x = bb.min.x - margin;
+ float max_x = bb.max.x + margin;
+ float min_y = bb.min.y - margin;
+ float max_y = bb.max.y + margin;
+
+ ::glBegin(GL_QUADS);
+ ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
+ ::glVertex3f(min_x, min_y, m_z);
+ ::glVertex3f(max_x, min_y, m_z);
+ ::glVertex3f(max_x, max_y, m_z);
+ ::glVertex3f(min_x, max_y, m_z);
+ ::glEnd();
+
+ ::glEnable(GL_CULL_FACE);
+ ::glDisable(GL_BLEND);
+ }
+}
+
+void GLCanvas3D::CuttingPlane::_render_contour() const
+{
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+
+ if (m_z >= 0.0f)
+ {
+ unsigned int lines_vcount = m_lines.get_vertices_count();
+
+ ::glLineWidth(2.0f);
+ ::glColor3f(0.0f, 0.0f, 0.0f);
+ ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_vertices());
+ ::glDrawArrays(GL_LINES, 0, (GLsizei)lines_vcount);
+ }
+
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+GLCanvas3D::Shader::Shader()
+ : m_shader(nullptr)
+{
+}
+
+GLCanvas3D::Shader::~Shader()
+{
+ _reset();
+}
+
+bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
+{
+ if (is_initialized())
+ return true;
+
+ m_shader = new GLShader();
+ if (m_shader != nullptr)
+ {
+ if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str()))
+ {
+ std::cout << "Compilaton of shader failed:" << std::endl;
+ std::cout << m_shader->last_error << std::endl;
+ _reset();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool GLCanvas3D::Shader::is_initialized() const
+{
+ return (m_shader != nullptr);
+}
+
+bool GLCanvas3D::Shader::start_using() const
+{
+ if (is_initialized())
+ {
+ m_shader->enable();
+ return true;
+ }
+ else
+ return false;
+}
+
+void GLCanvas3D::Shader::stop_using() const
+{
+ if (m_shader != nullptr)
+ m_shader->disable();
+}
+
+void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
+{
+ if (m_shader != nullptr)
+ m_shader->set_uniform(name.c_str(), value);
+}
+
+const GLShader* GLCanvas3D::Shader::get_shader() const
+{
+ return m_shader;
+}
+
+void GLCanvas3D::Shader::_reset()
+{
+ if (m_shader != nullptr)
+ {
+ m_shader->release();
+ delete m_shader;
+ m_shader = nullptr;
+ }
+}
+
+GLCanvas3D::LayersEditing::LayersEditing()
+ : m_use_legacy_opengl(false)
+ , m_enabled(false)
+ , m_z_texture_id(0)
+ , state(Unknown)
+ , band_width(2.0f)
+ , strength(0.005f)
+ , last_object_id(-1)
+ , last_z(0.0f)
+ , last_action(0)
+{
+}
+
+GLCanvas3D::LayersEditing::~LayersEditing()
+{
+ if (m_z_texture_id != 0)
+ {
+ ::glDeleteTextures(1, &m_z_texture_id);
+ m_z_texture_id = 0;
+ }
+}
+
+bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
+{
+ if (!m_shader.init(vertex_shader_filename, fragment_shader_filename))
+ return false;
+
+ ::glGenTextures(1, (GLuint*)&m_z_texture_id);
+ ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ return true;
+}
+
+bool GLCanvas3D::LayersEditing::is_allowed() const
+{
+ return !m_use_legacy_opengl && m_shader.is_initialized();
+}
+
+void GLCanvas3D::LayersEditing::set_use_legacy_opengl(bool use_legacy_opengl)
+{
+ m_use_legacy_opengl = use_legacy_opengl;
+}
+
+bool GLCanvas3D::LayersEditing::is_enabled() const
+{
+ return m_enabled;
+}
+
+void GLCanvas3D::LayersEditing::set_enabled(bool enabled)
+{
+ m_enabled = is_allowed() && enabled;
+}
+
+unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const
+{
+ return m_z_texture_id;
+}
+
+void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const
+{
+ if (!m_enabled)
+ return;
+
+ const Rect& bar_rect = get_bar_rect_viewport(canvas);
+ const Rect& reset_rect = get_reset_rect_viewport(canvas);
+
+ ::glDisable(GL_DEPTH_TEST);
+
+ // The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth),
+ // where x, y is the window size divided by $self->_zoom.
+ ::glPushMatrix();
+ ::glLoadIdentity();
+
+ _render_tooltip_texture(canvas, bar_rect, reset_rect);
+ _render_reset_texture(reset_rect);
+ _render_active_object_annotations(canvas, volume, print_object, bar_rect);
+ _render_profile(print_object, bar_rect);
+
+ // Revert the matrices.
+ ::glPopMatrix();
+
+ ::glEnable(GL_DEPTH_TEST);
+}
+
+int GLCanvas3D::LayersEditing::get_shader_program_id() const
+{
+ const GLShader* shader = m_shader.get_shader();
+ return (shader != nullptr) ? shader->shader_program_id : -1;
+}
+
+float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
+{
+ const Point& mouse_pos = canvas.get_local_mouse_position();
+ const Rect& rect = get_bar_rect_screen(canvas);
+ float x = (float)mouse_pos.x;
+ float y = (float)mouse_pos.y;
+ float t = rect.get_top();
+ float b = rect.get_bottom();
+
+ return ((rect.get_left() <= x) && (x <= rect.get_right()) && (t <= y) && (y <= b)) ?
+ // Inside the bar.
+ (b - y - 1.0f) / (b - t - 1.0f) :
+ // Outside the bar.
+ -1000.0f;
+}
+
+bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y)
+{
+ const Rect& rect = get_bar_rect_screen(canvas);
+ return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
+}
+
+bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y)
+{
+ const Rect& rect = get_reset_rect_screen(canvas);
+ return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
+}
+
+Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
+{
+ const Size& cnv_size = canvas.get_canvas_size();
+ float w = (float)cnv_size.get_width();
+ float h = (float)cnv_size.get_height();
+
+ return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT);
+}
+
+Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
+{
+ const Size& cnv_size = canvas.get_canvas_size();
+ float w = (float)cnv_size.get_width();
+ float h = (float)cnv_size.get_height();
+
+ return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h);
+}
+
+Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
+{
+ const Size& cnv_size = canvas.get_canvas_size();
+ float half_w = 0.5f * (float)cnv_size.get_width();
+ float half_h = 0.5f * (float)cnv_size.get_height();
+
+ float zoom = canvas.get_camera_zoom();
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+
+ return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom);
+}
+
+Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas)
+{
+ const Size& cnv_size = canvas.get_canvas_size();
+ float half_w = 0.5f * (float)cnv_size.get_width();
+ float half_h = 0.5f * (float)cnv_size.get_height();
+
+ float zoom = canvas.get_camera_zoom();
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+
+ return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
+}
+
+
+bool GLCanvas3D::LayersEditing::_is_initialized() const
+{
+ return m_shader.is_initialized();
+}
+
+void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
+{
+ if (m_tooltip_texture.get_id() == 0)
+ {
+ std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png";
+ if (!m_tooltip_texture.load_from_file(filename, false))
+ return;
+ }
+
+ float zoom = canvas.get_camera_zoom();
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+ float gap = 10.0f * inv_zoom;
+
+ float bar_left = bar_rect.get_left();
+ float reset_bottom = reset_rect.get_bottom();
+
+ float l = bar_left - (float)m_tooltip_texture.get_width() * inv_zoom - gap;
+ float r = bar_left - gap;
+ float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap;
+ float b = reset_bottom + gap;
+
+ GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t);
+}
+
+void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) const
+{
+ if (m_reset_texture.get_id() == 0)
+ {
+ std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png";
+ if (!m_reset_texture.load_from_file(filename, false))
+ return;
+ }
+
+ GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top());
+}
+
+void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const
+{
+ float max_z = print_object.model_object()->bounding_box().max.z;
+
+ m_shader.start_using();
+
+ m_shader.set_uniform("z_to_texture_row", (float)volume.layer_height_texture_z_to_row_id());
+ m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height());
+ m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas));
+ m_shader.set_uniform("z_cursor_band_width", band_width);
+
+ GLsizei w = (GLsizei)volume.layer_height_texture_width();
+ GLsizei h = (GLsizei)volume.layer_height_texture_height();
+ GLsizei half_w = w / 2;
+ GLsizei half_h = h / 2;
+
+ ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ ::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0());
+ ::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1());
+
+ // Render the color bar
+ float l = bar_rect.get_left();
+ float r = bar_rect.get_right();
+ float t = bar_rect.get_top();
+ float b = bar_rect.get_bottom();
+
+ ::glBegin(GL_QUADS);
+ ::glVertex3f(l, b, 0.0f);
+ ::glVertex3f(r, b, 0.0f);
+ ::glVertex3f(r, t, max_z);
+ ::glVertex3f(l, t, max_z);
+ ::glEnd();
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ m_shader.stop_using();
+}
+
+void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, const Rect& bar_rect) const
+{
+ // FIXME show some kind of legend.
+
+ // Get a maximum layer height value.
+ // FIXME This is a duplicate code of Slicing.cpp.
+ double layer_height_max = DBL_MAX;
+ const PrintConfig& print_config = print_object.print()->config;
+ const std::vector<double>& nozzle_diameters = dynamic_cast<const ConfigOptionFloats*>(print_config.option("nozzle_diameter"))->values;
+ const std::vector<double>& layer_heights_min = dynamic_cast<const ConfigOptionFloats*>(print_config.option("min_layer_height"))->values;
+ const std::vector<double>& layer_heights_max = dynamic_cast<const ConfigOptionFloats*>(print_config.option("max_layer_height"))->values;
+ for (unsigned int i = 0; i < (unsigned int)nozzle_diameters.size(); ++i)
+ {
+ double lh_min = (layer_heights_min[i] == 0.0) ? 0.07 : std::max(0.01, layer_heights_min[i]);
+ double lh_max = (layer_heights_max[i] == 0.0) ? (0.75 * nozzle_diameters[i]) : layer_heights_max[i];
+ layer_height_max = std::min(layer_height_max, std::max(lh_min, lh_max));
+ }
+
+ // Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region.
+ layer_height_max *= 1.12;
+
+ coordf_t max_z = unscale(print_object.size.z);
+ double layer_height = dynamic_cast<const ConfigOptionFloat*>(print_object.config.option("layer_height"))->value;
+ float l = bar_rect.get_left();
+ float w = bar_rect.get_right() - l;
+ float b = bar_rect.get_bottom();
+ float t = bar_rect.get_top();
+ float h = t - b;
+ float scale_x = w / (float)layer_height_max;
+ float scale_y = h / (float)max_z;
+ float x = l + (float)layer_height * scale_x;
+
+ // Baseline
+ ::glColor3f(0.0f, 0.0f, 0.0f);
+ ::glBegin(GL_LINE_STRIP);
+ ::glVertex2f(x, b);
+ ::glVertex2f(x, t);
+ ::glEnd();
+
+ // Curve
+ const ModelObject* model_object = print_object.model_object();
+ if (model_object->layer_height_profile_valid)
+ {
+ const std::vector<coordf_t>& profile = model_object->layer_height_profile;
+
+ ::glColor3f(0.0f, 0.0f, 1.0f);
+ ::glBegin(GL_LINE_STRIP);
+ for (unsigned int i = 0; i < profile.size(); i += 2)
+ {
+ ::glVertex2f(l + (float)profile[i + 1] * scale_x, b + (float)profile[i] * scale_y);
+ }
+ ::glEnd();
+ }
+}
+
+const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX);
+const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX);
+
+GLCanvas3D::Mouse::Drag::Drag()
+ : start_position_2D(Invalid_2D_Point)
+ , start_position_3D(Invalid_3D_Point)
+ , volume_idx(-1)
+{
+}
+
+GLCanvas3D::Mouse::Mouse()
+ : dragging(false)
+ , position(DBL_MAX, DBL_MAX)
+{
+}
+
+void GLCanvas3D::Mouse::set_start_position_2D_as_invalid()
+{
+ drag.start_position_2D = Drag::Invalid_2D_Point;
+}
+
+void GLCanvas3D::Mouse::set_start_position_3D_as_invalid()
+{
+ drag.start_position_3D = Drag::Invalid_3D_Point;
+}
+
+bool GLCanvas3D::Mouse::is_start_position_2D_defined() const
+{
+ return (drag.start_position_2D != Drag::Invalid_2D_Point);
+}
+
+bool GLCanvas3D::Mouse::is_start_position_3D_defined() const
+{
+ return (drag.start_position_3D != Drag::Invalid_3D_Point);
+}
+
+const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f;
+const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f;
+
+GLCanvas3D::Gizmos::Gizmos()
+ : m_enabled(false)
+ , m_current(Undefined)
+ , m_dragging(false)
+{
+}
+
+GLCanvas3D::Gizmos::~Gizmos()
+{
+ _reset();
+}
+
+bool GLCanvas3D::Gizmos::init()
+{
+ GLGizmoBase* gizmo = new GLGizmoScale;
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init())
+ return false;
+
+ m_gizmos.insert(GizmosMap::value_type(Scale, gizmo));
+
+ gizmo = new GLGizmoRotate;
+ if (gizmo == nullptr)
+ {
+ _reset();
+ return false;
+ }
+
+ if (!gizmo->init())
+ {
+ _reset();
+ return false;
+ }
+
+ m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
+
+ return true;
+}
+
+bool GLCanvas3D::Gizmos::is_enabled() const
+{
+ return m_enabled;
+}
+
+void GLCanvas3D::Gizmos::set_enabled(bool enable)
+{
+ m_enabled = enable;
+}
+
+void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos)
+{
+ if (!m_enabled)
+ return;
+
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float height = _get_total_overlay_height();
+ float top_y = 0.5f * (cnv_h - height);
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if (it->second == nullptr)
+ continue;
+
+ float tex_size = (float)it->second->get_textures_size();
+ float half_tex_size = 0.5f * tex_size;
+
+ // we currently use circular icons for gizmo, so we check the radius
+ if (it->second->get_state() != GLGizmoBase::On)
+ {
+ bool inside = length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size;
+ it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
+ }
+ top_y += (tex_size + OverlayGapY);
+ }
+}
+
+void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos)
+{
+ if (!m_enabled)
+ return;
+
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float height = _get_total_overlay_height();
+ float top_y = 0.5f * (cnv_h - height);
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if (it->second == nullptr)
+ continue;
+
+ float tex_size = (float)it->second->get_textures_size();
+ float half_tex_size = 0.5f * tex_size;
+
+ // we currently use circular icons for gizmo, so we check the radius
+ if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size)
+ {
+ if ((it->second->get_state() == GLGizmoBase::On))
+ {
+ it->second->set_state(GLGizmoBase::Off);
+ m_current = Undefined;
+ }
+ else
+ {
+ it->second->set_state(GLGizmoBase::On);
+ m_current = it->first;
+ }
+ }
+ else
+ it->second->set_state(GLGizmoBase::Off);
+
+ top_y += (tex_size + OverlayGapY);
+ }
+}
+
+void GLCanvas3D::Gizmos::reset_all_states()
+{
+ if (!m_enabled)
+ return;
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if (it->second != nullptr)
+ {
+ it->second->set_state(GLGizmoBase::Off);
+ it->second->set_hover_id(-1);
+ }
+ }
+
+ m_current = Undefined;
+}
+
+void GLCanvas3D::Gizmos::set_hover_id(int id)
+{
+ if (!m_enabled)
+ return;
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On))
+ it->second->set_hover_id(id);
+ }
+}
+
+bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const
+{
+ if (!m_enabled)
+ return false;
+
+ float cnv_h = (float)canvas.get_canvas_size().get_height();
+ float height = _get_total_overlay_height();
+ float top_y = 0.5f * (cnv_h - height);
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ if (it->second == nullptr)
+ continue;
+
+ float tex_size = (float)it->second->get_textures_size();
+ float half_tex_size = 0.5f * tex_size;
+
+ // we currently use circular icons for gizmo, so we check the radius
+ if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size)
+ return true;
+
+ top_y += (tex_size + OverlayGapY);
+ }
+
+ return false;
+}
+
+bool GLCanvas3D::Gizmos::grabber_contains_mouse() const
+{
+ if (!m_enabled)
+ return false;
+
+ GLGizmoBase* curr = _get_current();
+ return (curr != nullptr) ? (curr->get_hover_id() != -1) : false;
+}
+
+void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos)
+{
+ if (!m_enabled)
+ return;
+
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->update(mouse_pos);
+}
+
+void GLCanvas3D::Gizmos::update_data(float scale)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Scale);
+ if (it != m_gizmos.end())
+ reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
+}
+
+bool GLCanvas3D::Gizmos::is_running() const
+{
+ if (!m_enabled)
+ return false;
+
+ GLGizmoBase* curr = _get_current();
+ return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false;
+}
+
+bool GLCanvas3D::Gizmos::is_dragging() const
+{
+ return m_dragging;
+}
+
+void GLCanvas3D::Gizmos::start_dragging()
+{
+ m_dragging = true;
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->start_dragging();
+}
+
+void GLCanvas3D::Gizmos::stop_dragging()
+{
+ m_dragging = false;
+}
+
+float GLCanvas3D::Gizmos::get_scale() const
+{
+ if (!m_enabled)
+ return 1.0f;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Scale);
+ return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale*>(it->second)->get_scale() : 1.0f;
+}
+
+void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
+{
+ if (!m_enabled)
+ return;
+
+ ::glDisable(GL_DEPTH_TEST);
+
+ _render_current_gizmo(box);
+
+ ::glPushMatrix();
+ ::glLoadIdentity();
+
+ _render_overlay(canvas);
+
+ ::glPopMatrix();
+}
+
+void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const
+{
+ if (!m_enabled)
+ return;
+
+ ::glDisable(GL_DEPTH_TEST);
+
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->render_for_picking(box);
+}
+
+void GLCanvas3D::Gizmos::_reset()
+{
+ for (GizmosMap::value_type& gizmo : m_gizmos)
+ {
+ delete gizmo.second;
+ gizmo.second = nullptr;
+ }
+
+ m_gizmos.clear();
+}
+
+void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const
+{
+ if (m_gizmos.empty())
+ return;
+
+ float cnv_w = (float)canvas.get_canvas_size().get_width();
+ float zoom = canvas.get_camera_zoom();
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+
+ float height = _get_total_overlay_height();
+ float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom;
+ float top_y = 0.5f * height * inv_zoom;
+ float scaled_gap_y = OverlayGapY * inv_zoom;
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ float tex_size = (float)it->second->get_textures_size() * inv_zoom;
+ GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
+ top_y -= (tex_size + scaled_gap_y);
+ }
+}
+
+void GLCanvas3D::Gizmos::_render_current_gizmo(const BoundingBoxf3& box) const
+{
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->render(box);
+}
+
+float GLCanvas3D::Gizmos::_get_total_overlay_height() const
+{
+ float height = 0.0f;
+
+ for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
+ {
+ height += (float)it->second->get_textures_size();
+ if (std::distance(it, m_gizmos.end()) > 1)
+ height += OverlayGapY;
+ }
+
+ return height;
+}
+
+GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
+{
+ GizmosMap::const_iterator it = m_gizmos.find(m_current);
+ return (it != m_gizmos.end()) ? it->second : nullptr;
+}
+
+GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context)
+ : m_canvas(canvas)
+ , m_context(context)
+ , m_timer(nullptr)
+ , m_config(nullptr)
+ , m_print(nullptr)
+ , m_model(nullptr)
+ , m_dirty(true)
+ , m_active(true)
+ , m_initialized(false)
+ , m_use_VBOs(false)
+ , m_force_zoom_to_bed_enabled(false)
+ , m_apply_zoom_to_volumes_filter(false)
+ , m_hover_volume_id(-1)
+ , m_warning_texture_enabled(false)
+ , m_legend_texture_enabled(false)
+ , m_picking_enabled(false)
+ , m_moving_enabled(false)
+ , m_shader_enabled(false)
+ , m_multisample_allowed(false)
+ , m_color_by("volume")
+ , m_select_by("object")
+ , m_drag_by("instance")
+ , m_reload_delayed(false)
+{
+ if (m_canvas != nullptr)
+ m_timer = new wxTimer(m_canvas);
+}
+
+GLCanvas3D::~GLCanvas3D()
+{
+ reset_volumes();
+
+ if (m_timer != nullptr)
+ {
+ delete m_timer;
+ m_timer = nullptr;
+ }
+
+ _deregister_callbacks();
+}
+
+bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl)
+{
+ if (m_initialized)
+ return true;
+
+ ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ ::glClearDepth(1.0f);
+
+ ::glDepthFunc(GL_LESS);
+
+ ::glEnable(GL_DEPTH_TEST);
+ ::glEnable(GL_CULL_FACE);
+ ::glEnable(GL_BLEND);
+ ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Set antialiasing / multisampling
+ ::glDisable(GL_LINE_SMOOTH);
+ ::glDisable(GL_POLYGON_SMOOTH);
+
+ // ambient lighting
+ GLfloat ambient[4] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ ::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+
+ ::glEnable(GL_LIGHT0);
+ ::glEnable(GL_LIGHT1);
+
+ // light from camera
+ GLfloat specular_cam[4] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular_cam);
+ GLfloat diffuse_cam[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
+ ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_cam);
+
+ // light from above
+ GLfloat specular_top[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
+ ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular_top);
+ GLfloat diffuse_top[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
+ ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_top);
+
+ // Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
+ ::glShadeModel(GL_SMOOTH);
+
+ // A handy trick -- have surface material mirror the color.
+ ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
+ ::glEnable(GL_COLOR_MATERIAL);
+
+ if (m_multisample_allowed)
+ ::glEnable(GL_MULTISAMPLE);
+
+ if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs"))
+ return false;
+
+ if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs"))
+ return false;
+
+ m_use_VBOs = useVBOs;
+ m_layers_editing.set_use_legacy_opengl(use_legacy_opengl);
+
+ // on linux the gl context is not valid until the canvas is not shown on screen
+ // we defer the geometry finalization of volumes until the first call to render()
+ if (!m_volumes.empty())
+ m_volumes.finalize_geometry(m_use_VBOs);
+
+ if (m_gizmos.is_enabled() && !m_gizmos.init())
+ return false;
+
+ m_initialized = true;
+
+ return true;
+}
+
+bool GLCanvas3D::set_current()
+{
+ if ((m_canvas != nullptr) && (m_context != nullptr))
+ {
+ m_canvas->SetCurrent(*m_context);
+ return true;
+ }
+
+ return false;
+}
+
+void GLCanvas3D::set_active(bool active)
+{
+ m_active = active;
+}
+
+unsigned int GLCanvas3D::get_volumes_count() const
+{
+ return (unsigned int)m_volumes.volumes.size();
+}
+
+void GLCanvas3D::reset_volumes()
+{
+ if (set_current())
+ {
+ m_volumes.release_geometry();
+ m_volumes.clear();
+ m_dirty = true;
+ }
+}
+
+void GLCanvas3D::deselect_volumes()
+{
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ if (vol != nullptr)
+ vol->selected = false;
+ }
+}
+
+void GLCanvas3D::select_volume(unsigned int id)
+{
+ if (id < (unsigned int)m_volumes.volumes.size())
+ {
+ GLVolume* vol = m_volumes.volumes[id];
+ if (vol != nullptr)
+ vol->selected = true;
+ }
+}
+
+void GLCanvas3D::update_volumes_selection(const std::vector<int>& selections)
+{
+ if (m_model == nullptr)
+ return;
+
+ for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx)
+ {
+ if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size()))
+ {
+ const std::vector<int>& volume_idxs = m_objects_volumes_idxs[obj_idx];
+ for (int v : volume_idxs)
+ {
+ select_volume(v);
+ }
+ }
+ }
+}
+
+bool GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
+{
+ return m_volumes.check_outside_state(config);
+}
+
+bool GLCanvas3D::move_volume_up(unsigned int id)
+{
+ if ((id > 0) && (id < (unsigned int)m_volumes.volumes.size()))
+ {
+ std::swap(m_volumes.volumes[id - 1], m_volumes.volumes[id]);
+ std::swap(m_volumes.volumes[id - 1]->composite_id, m_volumes.volumes[id]->composite_id);
+ std::swap(m_volumes.volumes[id - 1]->select_group_id, m_volumes.volumes[id]->select_group_id);
+ std::swap(m_volumes.volumes[id - 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id);
+ return true;
+ }
+
+ return false;
+}
+
+bool GLCanvas3D::move_volume_down(unsigned int id)
+{
+ if ((id >= 0) && (id + 1 < (unsigned int)m_volumes.volumes.size()))
+ {
+ std::swap(m_volumes.volumes[id + 1], m_volumes.volumes[id]);
+ std::swap(m_volumes.volumes[id + 1]->composite_id, m_volumes.volumes[id]->composite_id);
+ std::swap(m_volumes.volumes[id + 1]->select_group_id, m_volumes.volumes[id]->select_group_id);
+ std::swap(m_volumes.volumes[id + 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id);
+ return true;
+ }
+
+ return false;
+}
+
+void GLCanvas3D::set_objects_selections(const std::vector<int>& selections)
+{
+ m_objects_selections = selections;
+}
+
+void GLCanvas3D::set_config(DynamicPrintConfig* config)
+{
+ m_config = config;
+}
+
+void GLCanvas3D::set_print(Print* print)
+{
+ m_print = print;
+}
+
+void GLCanvas3D::set_model(Model* model)
+{
+ m_model = model;
+}
+
+void GLCanvas3D::set_bed_shape(const Pointfs& shape)
+{
+ m_bed.set_shape(shape);
+
+ // Set the origin and size for painting of the coordinate system axes.
+ m_axes.origin = Pointf3(0.0, 0.0, (coordf_t)GROUND_Z);
+ set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size());
+}
+
+void GLCanvas3D::set_auto_bed_shape()
+{
+ // draw a default square bed around object center
+ const BoundingBoxf3& bbox = volumes_bounding_box();
+ coordf_t max_size = bbox.max_size();
+ const Pointf3& center = bbox.center();
+
+ Pointfs bed_shape;
+ bed_shape.reserve(4);
+ bed_shape.emplace_back(center.x - max_size, center.y - max_size);
+ bed_shape.emplace_back(center.x + max_size, center.y - max_size);
+ bed_shape.emplace_back(center.x + max_size, center.y + max_size);
+ bed_shape.emplace_back(center.x - max_size, center.y + max_size);
+
+ set_bed_shape(bed_shape);
+
+ // Set the origin for painting of the coordinate system axes.
+ m_axes.origin = Pointf3(center.x, center.y, (coordf_t)GROUND_Z);
+}
+
+void GLCanvas3D::set_axes_length(float length)
+{
+ m_axes.length = length;
+}
+
+void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons)
+{
+ m_cutting_plane.set(z, polygons);
+}
+
+void GLCanvas3D::set_color_by(const std::string& value)
+{
+ m_color_by = value;
+}
+
+void GLCanvas3D::set_select_by(const std::string& value)
+{
+ m_select_by = value;
+}
+
+void GLCanvas3D::set_drag_by(const std::string& value)
+{
+ m_drag_by = value;
+}
+
+float GLCanvas3D::get_camera_zoom() const
+{
+ return m_camera.zoom;
+}
+
+BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
+{
+ BoundingBoxf3 bb;
+ for (const GLVolume* volume : m_volumes.volumes)
+ {
+ if (!m_apply_zoom_to_volumes_filter || ((volume != nullptr) && volume->zoom_to_volumes))
+ bb.merge(volume->transformed_bounding_box());
+ }
+ return bb;
+}
+
+bool GLCanvas3D::is_layers_editing_enabled() const
+{
+ return m_layers_editing.is_enabled();
+}
+
+bool GLCanvas3D::is_layers_editing_allowed() const
+{
+ return m_layers_editing.is_allowed();
+}
+
+bool GLCanvas3D::is_shader_enabled() const
+{
+ return m_shader_enabled;
+}
+
+bool GLCanvas3D::is_reload_delayed() const
+{
+ return m_reload_delayed;
+}
+
+void GLCanvas3D::enable_layers_editing(bool enable)
+{
+ m_layers_editing.set_enabled(enable);
+}
+
+void GLCanvas3D::enable_warning_texture(bool enable)
+{
+ m_warning_texture_enabled = enable;
+}
+
+void GLCanvas3D::enable_legend_texture(bool enable)
+{
+ m_legend_texture_enabled = enable;
+}
+
+void GLCanvas3D::enable_picking(bool enable)
+{
+ m_picking_enabled = enable;
+}
+
+void GLCanvas3D::enable_moving(bool enable)
+{
+ m_moving_enabled = enable;
+}
+
+void GLCanvas3D::enable_gizmos(bool enable)
+{
+ m_gizmos.set_enabled(enable);
+}
+
+void GLCanvas3D::enable_shader(bool enable)
+{
+ m_shader_enabled = enable;
+}
+
+void GLCanvas3D::enable_force_zoom_to_bed(bool enable)
+{
+ m_force_zoom_to_bed_enabled = enable;
+}
+
+void GLCanvas3D::allow_multisample(bool allow)
+{
+ m_multisample_allowed = allow;
+}
+
+void GLCanvas3D::zoom_to_bed()
+{
+ _zoom_to_bounding_box(m_bed.get_bounding_box());
+}
+
+void GLCanvas3D::zoom_to_volumes()
+{
+ m_apply_zoom_to_volumes_filter = true;
+ _zoom_to_bounding_box(volumes_bounding_box());
+ m_apply_zoom_to_volumes_filter = false;
+}
+
+void GLCanvas3D::select_view(const std::string& direction)
+{
+ const float* dir_vec = nullptr;
+
+ if (direction == "iso")
+ dir_vec = VIEW_DEFAULT;
+ else if (direction == "left")
+ dir_vec = VIEW_LEFT;
+ else if (direction == "right")
+ dir_vec = VIEW_RIGHT;
+ else if (direction == "top")
+ dir_vec = VIEW_TOP;
+ else if (direction == "bottom")
+ dir_vec = VIEW_BOTTOM;
+ else if (direction == "front")
+ dir_vec = VIEW_FRONT;
+ else if (direction == "rear")
+ dir_vec = VIEW_REAR;
+
+ if ((dir_vec != nullptr) && !empty(volumes_bounding_box()))
+ {
+ m_camera.phi = dir_vec[0];
+ m_camera.set_theta(dir_vec[1]);
+
+ m_on_viewport_changed_callback.call();
+
+ if (m_canvas != nullptr)
+ m_canvas->Refresh();
+ }
+}
+
+void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other)
+{
+ m_camera.phi = other.m_camera.phi;
+ m_camera.set_theta(other.m_camera.get_theta());
+ m_camera.target = other.m_camera.target;
+ m_camera.zoom = other.m_camera.zoom;
+ m_dirty = true;
+}
+
+void GLCanvas3D::update_volumes_colors_by_extruder()
+{
+ if (m_config != nullptr)
+ m_volumes.update_colors_by_extruder(m_config);
+}
+
+void GLCanvas3D::render()
+{
+ if (m_canvas == nullptr)
+ return;
+
+ if (!_is_shown_on_screen())
+ return;
+
+ // ensures that the proper context is selected and that this canvas is initialized
+ if (!set_current() || !_3DScene::init(m_canvas))
+ return;
+
+ if (m_force_zoom_to_bed_enabled)
+ _force_zoom_to_bed();
+
+ _camera_tranform();
+
+ GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f };
+ ::glLightfv(GL_LIGHT1, GL_POSITION, position_cam);
+ GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f };
+ ::glLightfv(GL_LIGHT0, GL_POSITION, position_top);
+
+ float theta = m_camera.get_theta();
+ bool is_custom_bed = m_bed.is_custom();
+
+ _picking_pass();
+ _render_background();
+ // untextured bed needs to be rendered before objects
+ if (is_custom_bed)
+ {
+ _render_bed(theta);
+ // disable depth testing so that axes are not covered by ground
+ _render_axes(false);
+ }
+ _render_objects();
+ // textured bed needs to be rendered after objects
+ if (!is_custom_bed)
+ {
+ _render_axes(true);
+ _render_bed(theta);
+ }
+ _render_cutting_plane();
+ _render_warning_texture();
+ _render_legend_texture();
+ _render_gizmo();
+ _render_layer_editing_overlay();
+
+ m_canvas->SwapBuffers();
+}
+
+std::vector<double> GLCanvas3D::get_current_print_zs(bool active_only) const
+{
+ return m_volumes.get_current_print_zs(active_only);
+}
+
+void GLCanvas3D::set_toolpaths_range(double low, double high)
+{
+ m_volumes.set_range(low, high);
+}
+
+std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs)
+{
+ if (instance_idxs.empty())
+ {
+ for (unsigned int i = 0; i < model_object.instances.size(); ++i)
+ {
+ instance_idxs.push_back(i);
+ }
+ }
+ return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized);
+}
+
+std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
+{
+ if ((0 <= obj_idx) && (obj_idx < (int)model.objects.size()))
+ {
+ const ModelObject* model_object = model.objects[obj_idx];
+ if (model_object != nullptr)
+ return load_object(*model_object, obj_idx, std::vector<int>());
+ }
+
+ return std::vector<int>();
+}
+
+void GLCanvas3D::reload_scene(bool force)
+{
+ if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
+ return;
+
+ reset_volumes();
+ set_bed_shape(dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"))->values);
+
+ if (!m_canvas->IsShown() && !force)
+ {
+ m_reload_delayed = true;
+ return;
+ }
+
+ m_reload_delayed = false;
+
+ m_objects_volumes_idxs.clear();
+
+ for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx)
+ {
+ m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx));
+ }
+
+ update_volumes_selection(m_objects_selections);
+
+ if (m_config->has("nozzle_diameter"))
+ {
+ // Should the wipe tower be visualized ?
+ unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size();
+
+ bool semm = dynamic_cast<const ConfigOptionBool*>(m_config->option("single_extruder_multi_material"))->value;
+ bool wt = dynamic_cast<const ConfigOptionBool*>(m_config->option("wipe_tower"))->value;
+ bool co = dynamic_cast<const ConfigOptionBool*>(m_config->option("complete_objects"))->value;
+
+ if ((extruders_count > 1) && semm && wt && !co)
+ {
+ // Height of a print (Show at least a slab)
+ coordf_t height = std::max(m_model->bounding_box().max.z, 10.0);
+
+ float x = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_x"))->value;
+ float y = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_y"))->value;
+ float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
+ float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
+
+ m_volumes.load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized);
+ }
+ }
+
+ update_volumes_colors_by_extruder();
+
+ // checks for geometry outside the print volume to render it accordingly
+ if (!m_volumes.empty())
+ {
+ bool contained = m_volumes.check_outside_state(m_config);
+ if (!contained)
+ {
+ enable_warning_texture(true);
+ _3DScene::generate_warning_texture(L("Detected object outside print volume"));
+ m_on_enable_action_buttons_callback.call(false);
+ }
+ else
+ {
+ enable_warning_texture(false);
+ m_volumes.reset_outside_state();
+ _3DScene::reset_warning_texture();
+ m_on_enable_action_buttons_callback.call(!m_model->objects.empty());
+ }
+ }
+ else
+ {
+ enable_warning_texture(false);
+ _3DScene::reset_warning_texture();
+ }
+}
+
+void GLCanvas3D::load_print_toolpaths()
+{
+ if (m_print == nullptr)
+ return;
+
+ if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
+ return;
+
+ if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
+ return;
+
+ const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
+
+ // number of skirt layers
+ size_t total_layer_count = 0;
+ for (const PrintObject* print_object : m_print->objects)
+ {
+ total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
+ }
+ size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
+ if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
+ skirt_height = 1;
+
+ // get first skirt_height layers (maybe this should be moved to a PrintObject method?)
+ const PrintObject* object0 = m_print->objects.front();
+ std::vector<float> print_zs;
+ print_zs.reserve(skirt_height * 2);
+ for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
+ {
+ print_zs.push_back(float(object0->layers[i]->print_z));
+ }
+ //FIXME why there are support layers?
+ for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
+ {
+ print_zs.push_back(float(object0->support_layers[i]->print_z));
+ }
+ sort_remove_duplicates(print_zs);
+ if (print_zs.size() > skirt_height)
+ print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
+
+ m_volumes.volumes.emplace_back(new GLVolume(color));
+ GLVolume& volume = *m_volumes.volumes.back();
+ for (size_t i = 0; i < skirt_height; ++i) {
+ volume.print_zs.push_back(print_zs[i]);
+ volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
+ volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
+ if (i == 0)
+ _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
+
+ _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
+ }
+ volume.bounding_box = volume.indexed_vertex_array.bounding_box();
+ volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+}
+
+void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
+{
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ struct Ctxt
+ {
+ const Points *shifted_copies;
+ std::vector<const Layer*> layers;
+ bool has_perimeters;
+ bool has_infill;
+ bool has_support;
+ const std::vector<float>* tool_colors;
+
+ // Number of vertices (each vertex is 6x4=24 bytes long)
+ static const size_t alloc_size_max() { return 131072; } // 3.15MB
+ // static const size_t alloc_size_max () { return 65536; } // 1.57MB
+ // static const size_t alloc_size_max () { return 32768; } // 786kB
+ static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
+
+ static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
+ static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
+ static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
+
+ // For cloring by a tool, return a parsed color.
+ bool color_by_tool() const { return tool_colors != nullptr; }
+ size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
+ const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
+ int volume_idx(int extruder, int feature) const
+ {
+ return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
+ }
+ } ctxt;
+
+ ctxt.shifted_copies = &print_object._shifted_copies;
+
+ // order layers by print_z
+ ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
+ for (const Layer *layer : print_object.layers)
+ ctxt.layers.push_back(layer);
+ for (const Layer *layer : print_object.support_layers)
+ ctxt.layers.push_back(layer);
+ std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
+
+ // Maximum size of an allocation block: 32MB / sizeof(float)
+ ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
+ ctxt.has_infill = print_object.state.is_done(posInfill);
+ ctxt.has_support = print_object.state.is_done(posSupportMaterial);
+ ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
+
+ //FIXME Improve the heuristics for a grain size.
+ size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
+ tbb::spin_mutex new_volume_mutex;
+ auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
+ auto *volume = new GLVolume(color);
+ new_volume_mutex.lock();
+ volume->outside_printer_detection_enabled = false;
+ m_volumes.volumes.emplace_back(volume);
+ new_volume_mutex.unlock();
+ return volume;
+ };
+ const size_t volumes_cnt_initial = m_volumes.volumes.size();
+ std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
+ [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
+ std::vector<GLVolume*> vols;
+ if (ctxt.color_by_tool()) {
+ for (size_t i = 0; i < ctxt.number_tools(); ++i)
+ vols.emplace_back(new_volume(ctxt.color_tool(i)));
+ }
+ else
+ vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
+ for (GLVolume *vol : vols)
+ vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
+ const Layer *layer = ctxt.layers[idx_layer];
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
+ vol.print_zs.push_back(layer->print_z);
+ vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+ vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
+ }
+ }
+ for (const Point &copy : *ctxt.shifted_copies) {
+ for (const LayerRegion *layerm : layer->regions) {
+ if (ctxt.has_perimeters)
+ _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
+ if (ctxt.has_infill) {
+ for (const ExtrusionEntity *ee : layerm->fills.entities) {
+ // fill represents infill extrusions of a single island.
+ const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!fill->entities.empty())
+ _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(
+ is_solid_infill(fill->entities.front()->role()) ?
+ layerm->region()->config.solid_infill_extruder :
+ layerm->region()->config.infill_extruder,
+ 1)]);
+ }
+ }
+ }
+ if (ctxt.has_support) {
+ const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
+ if (support_layer) {
+ for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
+ _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(
+ (extrusion_entity->role() == erSupportMaterial) ?
+ support_layer->object()->config.support_material_extruder :
+ support_layer->object()->config.support_material_interface_extruder,
+ 2)]);
+ }
+ }
+ }
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
+ // Store the vertex arrays and restart their containers,
+ vols[i] = new_volume(vol.color);
+ GLVolume &vol_new = *vols[i];
+ // Assign the large pre-allocated buffers to the new GLVolume.
+ vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
+ // Copy the content back to the old GLVolume.
+ vol.indexed_vertex_array = vol_new.indexed_vertex_array;
+ // Finalize a bounding box of the old GLVolume.
+ vol.bounding_box = vol.indexed_vertex_array.bounding_box();
+ // Clear the buffers, but keep them pre-allocated.
+ vol_new.indexed_vertex_array.clear();
+ // Just make sure that clear did not clear the reserved memory.
+ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ }
+ }
+ }
+ for (GLVolume *vol : vols) {
+ vol->bounding_box = vol->indexed_vertex_array.bounding_box();
+ vol->indexed_vertex_array.shrink_to_fit();
+ }
+ });
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
+ // Remove empty volumes from the newly added volumes.
+ m_volumes.volumes.erase(
+ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
+ [](const GLVolume *volume) { return volume->empty(); }),
+ m_volumes.volumes.end());
+ for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
+ m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
+}
+
+void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
+{
+ if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
+ return;
+
+ if (!m_print->state.is_done(psWipeTower))
+ return;
+
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ struct Ctxt
+ {
+ const Print *print;
+ const std::vector<float> *tool_colors;
+
+ // Number of vertices (each vertex is 6x4=24 bytes long)
+ static const size_t alloc_size_max() { return 131072; } // 3.15MB
+ static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
+
+ static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
+
+ // For cloring by a tool, return a parsed color.
+ bool color_by_tool() const { return tool_colors != nullptr; }
+ size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
+ const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
+ int volume_idx(int tool, int feature) const
+ {
+ return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
+ }
+
+ const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
+ return priming.empty() ?
+ ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
+ ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
+ }
+ std::vector<WipeTower::ToolChangeResult> priming;
+ std::vector<WipeTower::ToolChangeResult> final;
+ } ctxt;
+
+ ctxt.print = m_print;
+ ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
+ if (m_print->m_wipe_tower_priming)
+ ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
+ if (m_print->m_wipe_tower_final_purge)
+ ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
+
+ //FIXME Improve the heuristics for a grain size.
+ size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
+ size_t grain_size = std::max(n_items / 128, size_t(1));
+ tbb::spin_mutex new_volume_mutex;
+ auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
+ auto *volume = new GLVolume(color);
+ new_volume_mutex.lock();
+ volume->outside_printer_detection_enabled = false;
+ m_volumes.volumes.emplace_back(volume);
+ new_volume_mutex.unlock();
+ return volume;
+ };
+ const size_t volumes_cnt_initial = m_volumes.volumes.size();
+ std::vector<GLVolumeCollection> volumes_per_thread(n_items);
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, n_items, grain_size),
+ [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
+ // Bounding box of this slab of a wipe tower.
+ std::vector<GLVolume*> vols;
+ if (ctxt.color_by_tool()) {
+ for (size_t i = 0; i < ctxt.number_tools(); ++i)
+ vols.emplace_back(new_volume(ctxt.color_tool(i)));
+ }
+ else
+ vols = { new_volume(ctxt.color_support()) };
+ for (GLVolume *volume : vols)
+ volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
+ const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
+ vol.print_zs.push_back(layer.front().print_z);
+ vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+ vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
+ }
+ }
+ for (const WipeTower::ToolChangeResult &extrusions : layer) {
+ for (size_t i = 1; i < extrusions.extrusions.size();) {
+ const WipeTower::Extrusion &e = extrusions.extrusions[i];
+ if (e.width == 0.) {
+ ++i;
+ continue;
+ }
+ size_t j = i + 1;
+ if (ctxt.color_by_tool())
+ for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
+ else
+ for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
+ size_t n_lines = j - i;
+ Lines lines;
+ std::vector<double> widths;
+ std::vector<double> heights;
+ lines.reserve(n_lines);
+ widths.reserve(n_lines);
+ heights.assign(n_lines, extrusions.layer_height);
+ for (; i < j; ++i) {
+ const WipeTower::Extrusion &e = extrusions.extrusions[i];
+ assert(e.width > 0.f);
+ const WipeTower::Extrusion &e_prev = *(&e - 1);
+ lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
+ widths.emplace_back(e.width);
+ }
+ _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
+ *vols[ctxt.volume_idx(e.tool, 0)]);
+ }
+ }
+ }
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
+ // Store the vertex arrays and restart their containers,
+ vols[i] = new_volume(vol.color);
+ GLVolume &vol_new = *vols[i];
+ // Assign the large pre-allocated buffers to the new GLVolume.
+ vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
+ // Copy the content back to the old GLVolume.
+ vol.indexed_vertex_array = vol_new.indexed_vertex_array;
+ // Finalize a bounding box of the old GLVolume.
+ vol.bounding_box = vol.indexed_vertex_array.bounding_box();
+ // Clear the buffers, but keep them pre-allocated.
+ vol_new.indexed_vertex_array.clear();
+ // Just make sure that clear did not clear the reserved memory.
+ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ }
+ }
+ for (GLVolume *vol : vols) {
+ vol->bounding_box = vol->indexed_vertex_array.bounding_box();
+ vol->indexed_vertex_array.shrink_to_fit();
+ }
+ });
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
+ // Remove empty volumes from the newly added volumes.
+ m_volumes.volumes.erase(
+ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
+ [](const GLVolume *volume) { return volume->empty(); }),
+ m_volumes.volumes.end());
+ for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
+ m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
+}
+
+void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
+{
+ if ((m_canvas != nullptr) && (m_print != nullptr))
+ {
+ // ensures that the proper context is selected
+ if (!set_current())
+ return;
+
+ if (m_volumes.empty())
+ {
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ m_gcode_preview_volume_index.reset();
+
+ _load_gcode_extrusion_paths(preview_data, tool_colors);
+ _load_gcode_travel_paths(preview_data, tool_colors);
+ _load_gcode_retractions(preview_data);
+ _load_gcode_unretractions(preview_data);
+
+ if (m_volumes.empty())
+ _3DScene::reset_legend_texture();
+ else
+ {
+ _3DScene::generate_legend_texture(preview_data, tool_colors);
+
+ // removes empty volumes
+ m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(),
+ [](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end());
+
+ _load_shells();
+ }
+ }
+
+ _update_gcode_volumes_visibility(preview_data);
+ }
+}
+
+void GLCanvas3D::register_on_viewport_changed_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_viewport_changed_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_double_click_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_double_click_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_right_click_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_right_click_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_select_object_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_select_object_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_model_update_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_model_update_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_remove_object_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_remove_object_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_arrange_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_arrange_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_rotate_object_left_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_rotate_object_left_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_rotate_object_right_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_rotate_object_right_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_scale_object_uniformly_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_scale_object_uniformly_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_increase_objects_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_increase_objects_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_decrease_objects_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_decrease_objects_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_instance_moved_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_instance_moved_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_wipe_tower_moved_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_wipe_tower_moved_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_enable_action_buttons_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_enable_action_buttons_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_gizmo_scale_uniformly_callback.register_callback(callback);
+}
+
+void GLCanvas3D::bind_event_handlers()
+{
+ if (m_canvas != nullptr)
+ {
+ m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
+ m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
+ m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
+ m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this);
+ m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
+ m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_RIGHT_UP, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_MOTION, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_ENTER_WINDOW, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_LEAVE_WINDOW, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_LEFT_DCLICK, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
+ m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
+ m_canvas->Bind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this);
+ }
+}
+
+void GLCanvas3D::unbind_event_handlers()
+{
+ if (m_canvas != nullptr)
+ {
+ m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
+ m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
+ m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
+ m_canvas->Unbind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this);
+ m_canvas->Unbind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
+ m_canvas->Unbind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_RIGHT_UP, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_MOTION, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_ENTER_WINDOW, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_LEAVE_WINDOW, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_LEFT_DCLICK, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
+ m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
+ m_canvas->Unbind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this);
+ }
+}
+
+void GLCanvas3D::on_size(wxSizeEvent& evt)
+{
+ m_dirty = true;
+}
+
+void GLCanvas3D::on_idle(wxIdleEvent& evt)
+{
+ if (!m_dirty)
+ return;
+
+ _refresh_if_shown_on_screen();
+}
+
+void GLCanvas3D::on_char(wxKeyEvent& evt)
+{
+ if (evt.HasModifiers())
+ evt.Skip();
+ else
+ {
+ int keyCode = evt.GetKeyCode();
+ switch (keyCode - 48)
+ {
+ // numerical input
+ case 0: { select_view("iso"); break; }
+ case 1: { select_view("top"); break; }
+ case 2: { select_view("bottom"); break; }
+ case 3: { select_view("front"); break; }
+ case 4: { select_view("rear"); break; }
+ case 5: { select_view("left"); break; }
+ case 6: { select_view("right"); break; }
+ default:
+ {
+ // text input
+ switch (keyCode)
+ {
+ // key +
+ case 43: { m_on_increase_objects_callback.call(); break; }
+ // key -
+ case 45: { m_on_decrease_objects_callback.call(); break; }
+ // key A/a
+ case 65:
+ case 97: { m_on_arrange_callback.call(); break; }
+ // key B/b
+ case 66:
+ case 98: { zoom_to_bed(); break; }
+ // key L/l
+ case 76:
+ case 108: { m_on_rotate_object_left_callback.call(); break; }
+ // key R/r
+ case 82:
+ case 114: { m_on_rotate_object_right_callback.call(); break; }
+ // key S/s
+ case 83:
+ case 115: { m_on_scale_object_uniformly_callback.call(); break; }
+ // key Z/z
+ case 90:
+ case 122: { zoom_to_volumes(); break; }
+ default:
+ {
+ evt.Skip();
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
+{
+ // Ignore the wheel events if the middle button is pressed.
+ if (evt.MiddleIsDown())
+ return;
+
+ // Performs layers editing updates, if enabled
+ if (is_layers_editing_enabled())
+ {
+ int object_idx_selected = _get_first_selected_object_id();
+ if (object_idx_selected != -1)
+ {
+ // A volume is selected. Test, whether hovering over a layer thickness bar.
+ if (m_layers_editing.bar_rect_contains(*this, (float)evt.GetX(), (float)evt.GetY()))
+ {
+ // Adjust the width of the selection.
+ m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f);
+ if (m_canvas != nullptr)
+ m_canvas->Refresh();
+
+ return;
+ }
+ }
+ }
+
+ // Calculate the zoom delta and apply it to the current zoom factor
+ float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
+ zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f;
+ zoom = get_camera_zoom() / (1.0f - zoom);
+
+ // Don't allow to zoom too far outside the scene.
+ float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box());
+ if (zoom_min > 0.0f)
+ zoom = std::max(zoom, zoom_min * 0.8f);
+
+ m_camera.zoom = zoom;
+ m_on_viewport_changed_callback.call();
+
+ _refresh_if_shown_on_screen();
+}
+
+void GLCanvas3D::on_timer(wxTimerEvent& evt)
+{
+ if (m_layers_editing.state != LayersEditing::Editing)
+ return;
+
+ _perform_layer_editing_action();
+}
+
+void GLCanvas3D::on_mouse(wxMouseEvent& evt)
+{
+ Point pos(evt.GetX(), evt.GetY());
+
+ int selected_object_idx = _get_first_selected_object_id();
+ int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
+ m_layers_editing.last_object_id = layer_editing_object_idx;
+ bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position);
+
+ if (evt.Entering())
+ {
+#if defined(__WXMSW__) || defined(__linux__)
+ // On Windows and Linux needs focus in order to catch key events
+ if (m_canvas != nullptr)
+ m_canvas->SetFocus();
+
+ m_mouse.set_start_position_2D_as_invalid();
+#endif
+ }
+ else if (evt.LeftDClick() && (m_hover_volume_id != -1))
+ m_on_double_click_callback.call();
+ else if (evt.LeftDown() || evt.RightDown())
+ {
+ // If user pressed left or right button we first check whether this happened
+ // on a volume or not.
+ int volume_idx = m_hover_volume_id;
+ m_layers_editing.state = LayersEditing::Unknown;
+ if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos.x, pos.y))
+ {
+ // A volume is selected and the mouse is inside the layer thickness bar.
+ // Start editing the layer height.
+ m_layers_editing.state = LayersEditing::Editing;
+ _perform_layer_editing_action(&evt);
+ }
+ else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos.x, pos.y))
+ {
+ if (evt.LeftDown())
+ {
+ // A volume is selected and the mouse is inside the reset button.
+ m_print->get_object(layer_editing_object_idx)->reset_layer_height_profile();
+ // Index 2 means no editing, just wait for mouse up event.
+ m_layers_editing.state = LayersEditing::Completed;
+
+ m_dirty = true;
+ }
+ }
+ else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse)
+ {
+ m_gizmos.update_on_off_state(*this, m_mouse.position);
+ _update_gizmos_data();
+ m_dirty = true;
+ }
+ else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse())
+ {
+ _update_gizmos_data();
+ m_gizmos.start_dragging();
+ m_dirty = true;
+ }
+ else
+ {
+ // Select volume in this 3D canvas.
+ // Don't deselect a volume if layer editing is enabled. We want the object to stay selected
+ // during the scene manipulation.
+
+ if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled()))
+ {
+ if (volume_idx != -1)
+ {
+ deselect_volumes();
+ select_volume(volume_idx);
+ int group_id = m_volumes.volumes[volume_idx]->select_group_id;
+ if (group_id != -1)
+ {
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ if ((vol != nullptr) && (vol->select_group_id == group_id))
+ vol->selected = true;
+ }
+ }
+
+ if (m_gizmos.is_running())
+ _update_gizmos_data();
+
+ m_dirty = true;
+ }
+ }
+
+ // propagate event through callback
+ if (m_picking_enabled && (volume_idx != -1))
+ _on_select(volume_idx);
+
+ if (volume_idx != -1)
+ {
+ if (evt.LeftDown() && m_moving_enabled)
+ {
+ // The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate pos x, y,
+ // an converts the screen space coordinate to unscaled object space.
+ Pointf3 pos3d = (volume_idx == -1) ? Pointf3(DBL_MAX, DBL_MAX) : _mouse_to_3d(pos);
+
+ // Only accept the initial position, if it is inside the volume bounding box.
+ BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
+ volume_bbox.offset(1.0);
+ if (volume_bbox.contains(pos3d))
+ {
+ // The dragging operation is initiated.
+ m_mouse.drag.volume_idx = volume_idx;
+ m_mouse.drag.start_position_3D = pos3d;
+ // Remember the shift to to the object center.The object center will later be used
+ // to limit the object placement close to the bed.
+ m_mouse.drag.volume_center_offset = pos3d.vector_to(volume_bbox.center());
+ }
+ }
+ else if (evt.RightDown())
+ {
+ // if right clicking on volume, propagate event through callback
+ if (m_volumes.volumes[volume_idx]->hover)
+ m_on_right_click_callback.call(pos.x, pos.y);
+ }
+ }
+ }
+ }
+ else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1))
+ {
+ m_mouse.dragging = true;
+
+ // Get new position at the same Z of the initial click point.
+ float z0 = 0.0f;
+ float z1 = 1.0f;
+ Pointf3 cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D.z);
+
+ // Clip the new position, so the object center remains close to the bed.
+ cur_pos.translate(m_mouse.drag.volume_center_offset);
+ Point cur_pos2(scale_(cur_pos.x), scale_(cur_pos.y));
+ if (!m_bed.contains(cur_pos2))
+ {
+ Point ip = m_bed.point_projection(cur_pos2);
+ cur_pos.x = unscale(ip.x);
+ cur_pos.y = unscale(ip.y);
+ }
+ cur_pos.translate(m_mouse.drag.volume_center_offset.negative());
+
+ // Calculate the translation vector.
+ Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos);
+ // Get the volume being dragged.
+ GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx];
+ // Get all volumes belonging to the same group, if any.
+ std::vector<GLVolume*> volumes;
+ if (volume->drag_group_id == -1)
+ volumes.push_back(volume);
+ else
+ {
+ for (GLVolume* v : m_volumes.volumes)
+ {
+ if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id))
+ volumes.push_back(v);
+ }
+ }
+
+ // Apply new temporary volume origin and ignore Z.
+ for (GLVolume* v : volumes)
+ {
+ v->origin.translate(vector.x, vector.y, 0.0);
+ }
+
+ m_mouse.drag.start_position_3D = cur_pos;
+
+ m_dirty = true;
+ }
+ else if (evt.Dragging() && m_gizmos.is_dragging())
+ {
+ m_mouse.dragging = true;
+
+ const Pointf3& cur_pos = _mouse_to_bed_3d(pos);
+ m_gizmos.update(Pointf(cur_pos.x, cur_pos.y));
+
+ m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
+ m_dirty = true;
+ }
+ else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
+ {
+ m_mouse.dragging = true;
+
+ if ((m_layers_editing.state != LayersEditing::Unknown) && (layer_editing_object_idx != -1))
+ {
+ if (m_layers_editing.state == LayersEditing::Editing)
+ _perform_layer_editing_action(&evt);
+ }
+ else if (evt.LeftIsDown())
+ {
+ // if dragging over blank area with left button, rotate
+ if (m_mouse.is_start_position_3D_defined())
+ {
+ const Pointf3& orig = m_mouse.drag.start_position_3D;
+ m_camera.phi += (((float)pos.x - (float)orig.x) * TRACKBALLSIZE);
+ m_camera.set_theta(m_camera.get_theta() - ((float)pos.y - (float)orig.y) * TRACKBALLSIZE);
+
+ m_on_viewport_changed_callback.call();
+
+ m_dirty = true;
+ }
+ m_mouse.drag.start_position_3D = Pointf3((coordf_t)pos.x, (coordf_t)pos.y, 0.0);
+ }
+ else if (evt.MiddleIsDown() || evt.RightIsDown())
+ {
+ // If dragging over blank area with right button, pan.
+ if (m_mouse.is_start_position_2D_defined())
+ {
+ // get point in model space at Z = 0
+ float z = 0.0f;
+ const Pointf3& cur_pos = _mouse_to_3d(pos, &z);
+ Pointf3 orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z);
+ Pointf3 camera_target = m_camera.target;
+ camera_target.translate(orig.vector_to(cur_pos).negative());
+ m_camera.target = camera_target;
+
+ m_on_viewport_changed_callback.call();
+
+ m_dirty = true;
+ }
+
+ m_mouse.drag.start_position_2D = pos;
+ }
+ }
+ else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
+ {
+ if (m_layers_editing.state != LayersEditing::Unknown)
+ {
+ m_layers_editing.state = LayersEditing::Unknown;
+ _stop_timer();
+
+ if (layer_editing_object_idx != -1)
+ m_on_model_update_callback.call();
+ }
+ else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging)
+ {
+ // get all volumes belonging to the same group, if any
+ std::vector<int> volume_idxs;
+ int vol_id = m_mouse.drag.volume_idx;
+ int group_id = m_volumes.volumes[vol_id]->drag_group_id;
+ if (group_id == -1)
+ volume_idxs.push_back(vol_id);
+ else
+ {
+ for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
+ {
+ if (m_volumes.volumes[i]->drag_group_id == group_id)
+ volume_idxs.push_back(i);
+ }
+ }
+
+ _on_move(volume_idxs);
+ }
+ else if (!m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled())
+ {
+ // deselect and propagate event through callback
+ if (m_picking_enabled)
+ {
+ deselect_volumes();
+ _on_select(-1);
+ }
+ }
+ else if (evt.LeftUp() && m_gizmos.is_dragging())
+ {
+ m_gizmos.stop_dragging();
+ }
+
+ m_mouse.drag.volume_idx = -1;
+ m_mouse.set_start_position_3D_as_invalid();
+ m_mouse.set_start_position_2D_as_invalid();
+ m_mouse.dragging = false;
+ }
+ else if (evt.Moving())
+ {
+ m_mouse.position = Pointf((coordf_t)pos.x, (coordf_t)pos.y);
+ // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
+ if (m_picking_enabled)
+ m_dirty = true;
+ }
+ else
+ evt.Skip();
+}
+
+void GLCanvas3D::on_paint(wxPaintEvent& evt)
+{
+ render();
+}
+
+void GLCanvas3D::on_key_down(wxKeyEvent& evt)
+{
+ if (evt.HasModifiers())
+ evt.Skip();
+ else
+ {
+ int key = evt.GetKeyCode();
+ if (key == WXK_DELETE)
+ m_on_remove_object_callback.call();
+ else
+ evt.Skip();
+ }
+}
+
+Size GLCanvas3D::get_canvas_size() const
+{
+ int w = 0;
+ int h = 0;
+
+ if (m_canvas != nullptr)
+ m_canvas->GetSize(&w, &h);
+
+ return Size(w, h);
+}
+
+Point GLCanvas3D::get_local_mouse_position() const
+{
+ if (m_canvas == nullptr)
+ return Point();
+
+ wxPoint mouse_pos = m_canvas->ScreenToClient(wxGetMousePosition());
+ return Point(mouse_pos.x, mouse_pos.y);
+}
+
+bool GLCanvas3D::_is_shown_on_screen() const
+{
+ return (m_canvas != nullptr) ? m_active && m_canvas->IsShownOnScreen() : false;
+}
+
+void GLCanvas3D::_force_zoom_to_bed()
+{
+ zoom_to_bed();
+ m_force_zoom_to_bed_enabled = false;
+}
+
+void GLCanvas3D::_resize(unsigned int w, unsigned int h)
+{
+ if (m_context == nullptr)
+ return;
+
+ set_current();
+ ::glViewport(0, 0, w, h);
+
+ ::glMatrixMode(GL_PROJECTION);
+ ::glLoadIdentity();
+
+ const BoundingBoxf3& bbox = _max_bounding_box();
+
+ switch (m_camera.type)
+ {
+ case Camera::Ortho:
+ {
+ float w2 = w;
+ float h2 = h;
+ float two_zoom = 2.0f * get_camera_zoom();
+ if (two_zoom != 0.0f)
+ {
+ float inv_two_zoom = 1.0f / two_zoom;
+ w2 *= inv_two_zoom;
+ h2 *= inv_two_zoom;
+ }
+
+ // FIXME: calculate a tighter value for depth will improve z-fighting
+ float depth = 5.0f * (float)bbox.max_size();
+ ::glOrtho(-w2, w2, -h2, h2, -depth, depth);
+
+ break;
+ }
+// case Camera::Perspective:
+// {
+// float bbox_r = (float)bbox.radius();
+// float fov = PI * 45.0f / 180.0f;
+// float fov_tan = tan(0.5f * fov);
+// float cam_distance = 0.5f * bbox_r / fov_tan;
+// m_camera.distance = cam_distance;
+//
+// float nr = cam_distance - bbox_r * 1.1f;
+// float fr = cam_distance + bbox_r * 1.1f;
+// if (nr < 1.0f)
+// nr = 1.0f;
+//
+// if (fr < nr + 1.0f)
+// fr = nr + 1.0f;
+//
+// float h2 = fov_tan * nr;
+// float w2 = h2 * w / h;
+// ::glFrustum(-w2, w2, -h2, h2, nr, fr);
+//
+// break;
+// }
+ default:
+ {
+ throw std::runtime_error("Invalid camera type.");
+ break;
+ }
+ }
+
+ ::glMatrixMode(GL_MODELVIEW);
+
+ m_dirty = false;
+}
+
+BoundingBoxf3 GLCanvas3D::_max_bounding_box() const
+{
+ BoundingBoxf3 bb = m_bed.get_bounding_box();
+ bb.merge(volumes_bounding_box());
+ return bb;
+}
+
+BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const
+{
+ BoundingBoxf3 bb;
+ for (const GLVolume* volume : m_volumes.volumes)
+ {
+ if ((volume != nullptr) && volume->selected)
+ bb.merge(volume->transformed_bounding_box());
+ }
+ return bb;
+}
+
+void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox)
+{
+ // Calculate the zoom factor needed to adjust viewport to bounding box.
+ float zoom = _get_zoom_to_bounding_box_factor(bbox);
+ if (zoom > 0.0f)
+ {
+ m_camera.zoom = zoom;
+ // center view around bounding box center
+ m_camera.target = bbox.center();
+
+ m_on_viewport_changed_callback.call();
+
+ _refresh_if_shown_on_screen();
+ }
+}
+
+float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const
+{
+ float max_bb_size = bbox.max_size();
+ if (max_bb_size == 0.0f)
+ return -1.0f;
+
+ // project the bbox vertices on a plane perpendicular to the camera forward axis
+ // then calculates the vertices coordinate on this plane along the camera xy axes
+
+ // we need the view matrix, we let opengl calculate it (same as done in render())
+ _camera_tranform();
+
+ // get the view matrix back from opengl
+ GLfloat matrix[16];
+ ::glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
+
+ // camera axes
+ Pointf3 right((coordf_t)matrix[0], (coordf_t)matrix[4], (coordf_t)matrix[8]);
+ Pointf3 up((coordf_t)matrix[1], (coordf_t)matrix[5], (coordf_t)matrix[9]);
+ Pointf3 forward((coordf_t)matrix[2], (coordf_t)matrix[6], (coordf_t)matrix[10]);
+
+ Pointf3 bb_min = bbox.min;
+ Pointf3 bb_max = bbox.max;
+ Pointf3 bb_center = bbox.center();
+
+ // bbox vertices in world space
+ std::vector<Pointf3> vertices;
+ vertices.reserve(8);
+ vertices.push_back(bb_min);
+ vertices.emplace_back(bb_max.x, bb_min.y, bb_min.z);
+ vertices.emplace_back(bb_max.x, bb_max.y, bb_min.z);
+ vertices.emplace_back(bb_min.x, bb_max.y, bb_min.z);
+ vertices.emplace_back(bb_min.x, bb_min.y, bb_max.z);
+ vertices.emplace_back(bb_max.x, bb_min.y, bb_max.z);
+ vertices.push_back(bb_max);
+ vertices.emplace_back(bb_min.x, bb_max.y, bb_max.z);
+
+ coordf_t max_x = 0.0;
+ coordf_t max_y = 0.0;
+
+ // margin factor to give some empty space around the bbox
+ coordf_t margin_factor = 1.25;
+
+ for (const Pointf3 v : vertices)
+ {
+ // project vertex on the plane perpendicular to camera forward axis
+ Pointf3 pos(v.x - bb_center.x, v.y - bb_center.y, v.z - bb_center.z);
+ Pointf3 proj_on_plane = pos - dot(pos, forward) * forward;
+
+ // calculates vertex coordinate along camera xy axes
+ coordf_t x_on_plane = dot(proj_on_plane, right);
+ coordf_t y_on_plane = dot(proj_on_plane, up);
+
+ max_x = std::max(max_x, margin_factor * std::abs(x_on_plane));
+ max_y = std::max(max_y, margin_factor * std::abs(y_on_plane));
+ }
+
+ if ((max_x == 0.0) || (max_y == 0.0))
+ return -1.0f;
+
+ max_x *= 2.0;
+ max_y *= 2.0;
+
+ const Size& cnv_size = get_canvas_size();
+ return (float)std::min((coordf_t)cnv_size.get_width() / max_x, (coordf_t)cnv_size.get_height() / max_y);
+}
+
+void GLCanvas3D::_deregister_callbacks()
+{
+ m_on_viewport_changed_callback.deregister_callback();
+ m_on_double_click_callback.deregister_callback();
+ m_on_right_click_callback.deregister_callback();
+ m_on_select_object_callback.deregister_callback();
+ m_on_model_update_callback.deregister_callback();
+ m_on_remove_object_callback.deregister_callback();
+ m_on_arrange_callback.deregister_callback();
+ m_on_rotate_object_left_callback.deregister_callback();
+ m_on_rotate_object_right_callback.deregister_callback();
+ m_on_scale_object_uniformly_callback.deregister_callback();
+ m_on_increase_objects_callback.deregister_callback();
+ m_on_decrease_objects_callback.deregister_callback();
+ m_on_instance_moved_callback.deregister_callback();
+ m_on_wipe_tower_moved_callback.deregister_callback();
+ m_on_enable_action_buttons_callback.deregister_callback();
+ m_on_gizmo_scale_uniformly_callback.deregister_callback();
+}
+
+void GLCanvas3D::_mark_volumes_for_layer_height() const
+{
+ if (m_print == nullptr)
+ return;
+
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ int object_id = int(vol->select_group_id / 1000000);
+ int shader_id = m_layers_editing.get_shader_program_id();
+
+ if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected &&
+ vol->has_layer_height_texture() && (object_id < (int)m_print->objects.size()))
+ {
+ vol->set_layer_height_texture_data(m_layers_editing.get_z_texture_id(), shader_id,
+ m_print->get_object(object_id), _get_layers_editing_cursor_z_relative(), m_layers_editing.band_width);
+ }
+ else
+ vol->reset_layer_height_texture_data();
+ }
+}
+
+void GLCanvas3D::_refresh_if_shown_on_screen()
+{
+ if (_is_shown_on_screen())
+ {
+ const Size& cnv_size = get_canvas_size();
+ _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
+ if (m_canvas != nullptr)
+ m_canvas->Refresh();
+ }
+}
+
+void GLCanvas3D::_camera_tranform() const
+{
+ ::glMatrixMode(GL_MODELVIEW);
+ ::glLoadIdentity();
+
+ ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
+ ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw
+
+ Pointf3 neg_target = m_camera.target.negative();
+ ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z);
+}
+
+void GLCanvas3D::_picking_pass() const
+{
+ const Pointf& pos = m_mouse.position;
+
+ if (m_picking_enabled && !m_mouse.dragging && (pos != Pointf(DBL_MAX, DBL_MAX)))
+ {
+ // Render the object for picking.
+ // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing.
+ // Better to use software ray - casting on a bounding - box hierarchy.
+
+ if (m_multisample_allowed)
+ ::glDisable(GL_MULTISAMPLE);
+
+ ::glDisable(GL_LIGHTING);
+ ::glDisable(GL_BLEND);
+
+ ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ _render_volumes(true);
+ m_gizmos.render_current_gizmo_for_picking_pass(_selected_volumes_bounding_box());
+
+ if (m_multisample_allowed)
+ ::glEnable(GL_MULTISAMPLE);
+
+ const Size& cnv_size = get_canvas_size();
+
+ GLubyte color[4];
+ ::glReadPixels(pos.x, cnv_size.get_height() - pos.y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color);
+ int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256;
+
+ m_hover_volume_id = -1;
+
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ vol->hover = false;
+ }
+
+ if (volume_id < (int)m_volumes.volumes.size())
+ {
+ m_hover_volume_id = volume_id;
+ m_volumes.volumes[volume_id]->hover = true;
+ int group_id = m_volumes.volumes[volume_id]->select_group_id;
+ if (group_id != -1)
+ {
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ if (vol->select_group_id == group_id)
+ vol->hover = true;
+ }
+ }
+ m_gizmos.set_hover_id(-1);
+ }
+ else
+ m_gizmos.set_hover_id(254 - (int)color[2]);
+
+ // updates gizmos overlay
+ if (_get_first_selected_object_id() != -1)
+ m_gizmos.update_hover_state(*this, pos);
+ else
+ m_gizmos.reset_all_states();
+ }
+}
+
+void GLCanvas3D::_render_background() const
+{
+ ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
+
+ ::glDisable(GL_LIGHTING);
+
+ ::glPushMatrix();
+ ::glLoadIdentity();
+ ::glMatrixMode(GL_PROJECTION);
+ ::glPushMatrix();
+ ::glLoadIdentity();
+
+ // Draws a bluish bottom to top gradient over the complete screen.
+ ::glDisable(GL_DEPTH_TEST);
+
+ ::glBegin(GL_QUADS);
+ ::glColor3f(0.0f, 0.0f, 0.0f);
+ ::glVertex3f(-1.0f, -1.0f, 1.0f);
+ ::glVertex3f(1.0f, -1.0f, 1.0f);
+ ::glColor3f(COLOR[0], COLOR[1], COLOR[2]);
+ ::glVertex3f(1.0f, 1.0f, 1.0f);
+ ::glVertex3f(-1.0f, 1.0f, 1.0f);
+ ::glEnd();
+
+ ::glEnable(GL_DEPTH_TEST);
+
+ ::glPopMatrix();
+ ::glMatrixMode(GL_MODELVIEW);
+ ::glPopMatrix();
+}
+
+void GLCanvas3D::_render_bed(float theta) const
+{
+ m_bed.render(theta);
+}
+
+void GLCanvas3D::_render_axes(bool depth_test) const
+{
+ m_axes.render(depth_test);
+}
+
+void GLCanvas3D::_render_objects() const
+{
+ if (m_volumes.empty())
+ return;
+
+ ::glEnable(GL_LIGHTING);
+
+ if (!m_shader_enabled)
+ _render_volumes(false);
+ else if (m_use_VBOs)
+ {
+ if (m_picking_enabled)
+ {
+ _mark_volumes_for_layer_height();
+
+ if (m_config != nullptr)
+ {
+ const BoundingBoxf3& bed_bb = m_bed.get_bounding_box();
+ m_volumes.set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height"));
+ m_volumes.check_outside_state(m_config);
+ }
+ // do not cull backfaces to show broken geometry, if any
+ ::glDisable(GL_CULL_FACE);
+ }
+
+ m_shader.start_using();
+ m_volumes.render_VBOs();
+ m_shader.stop_using();
+
+ if (m_picking_enabled)
+ ::glEnable(GL_CULL_FACE);
+ }
+ else
+ {
+ // do not cull backfaces to show broken geometry, if any
+ if (m_picking_enabled)
+ ::glDisable(GL_CULL_FACE);
+
+ m_volumes.render_legacy();
+
+ if (m_picking_enabled)
+ ::glEnable(GL_CULL_FACE);
+ }
+}
+
+void GLCanvas3D::_render_cutting_plane() const
+{
+ m_cutting_plane.render(volumes_bounding_box());
+}
+
+void GLCanvas3D::_render_warning_texture() const
+{
+ if (!m_warning_texture_enabled)
+ return;
+
+ // If the warning texture has not been loaded into the GPU, do it now.
+ unsigned int tex_id = _3DScene::finalize_warning_texture();
+ if (tex_id > 0)
+ {
+ unsigned int w = _3DScene::get_warning_texture_width();
+ unsigned int h = _3DScene::get_warning_texture_height();
+ if ((w > 0) && (h > 0))
+ {
+ ::glDisable(GL_DEPTH_TEST);
+ ::glPushMatrix();
+ ::glLoadIdentity();
+
+ const Size& cnv_size = get_canvas_size();
+ float zoom = get_camera_zoom();
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+ float l = (-0.5f * (float)w) * inv_zoom;
+ float t = (-0.5f * (float)cnv_size.get_height() + (float)h) * inv_zoom;
+ float r = l + (float)w * inv_zoom;
+ float b = t - (float)h * inv_zoom;
+
+ GLTexture::render_texture(tex_id, l, r, b, t);
+
+ ::glPopMatrix();
+ ::glEnable(GL_DEPTH_TEST);
+ }
+ }
+}
+
+void GLCanvas3D::_render_legend_texture() const
+{
+ if (!m_legend_texture_enabled)
+ return;
+
+ // If the legend texture has not been loaded into the GPU, do it now.
+ unsigned int tex_id = _3DScene::finalize_legend_texture();
+ if (tex_id > 0)
+ {
+ unsigned int w = _3DScene::get_legend_texture_width();
+ unsigned int h = _3DScene::get_legend_texture_height();
+ if ((w > 0) && (h > 0))
+ {
+ ::glDisable(GL_DEPTH_TEST);
+ ::glPushMatrix();
+ ::glLoadIdentity();
+
+ const Size& cnv_size = get_canvas_size();
+ float zoom = get_camera_zoom();
+ float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
+ float l = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
+ float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
+ float r = l + (float)w * inv_zoom;
+ float b = t - (float)h * inv_zoom;
+ GLTexture::render_texture(tex_id, l, r, b, t);
+
+ ::glPopMatrix();
+ ::glEnable(GL_DEPTH_TEST);
+ }
+ }
+}
+
+void GLCanvas3D::_render_layer_editing_overlay() const
+{
+ if (m_print == nullptr)
+ return;
+
+ GLVolume* volume = nullptr;
+
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture())
+ {
+ volume = vol;
+ break;
+ }
+ }
+
+ if (volume == nullptr)
+ return;
+
+ // If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion
+ // and an update by Platter::async_apply_config.
+ int object_idx = int(volume->select_group_id / 1000000);
+ if ((int)m_print->objects.size() < object_idx)
+ return;
+
+ const PrintObject* print_object = m_print->get_object(object_idx);
+ if (print_object == nullptr)
+ return;
+
+ m_layers_editing.render(*this, *print_object, *volume);
+}
+
+void GLCanvas3D::_render_volumes(bool fake_colors) const
+{
+ static const GLfloat INV_255 = 1.0f / 255.0f;
+
+ if (fake_colors)
+ ::glDisable(GL_LIGHTING);
+ else
+ ::glEnable(GL_LIGHTING);
+
+ // do not cull backfaces to show broken geometry, if any
+ ::glDisable(GL_CULL_FACE);
+
+ ::glEnable(GL_BLEND);
+ ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+
+ unsigned int volume_id = 0;
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ if (fake_colors)
+ {
+ // Object picking mode. Render the object with a color encoding the object index.
+ unsigned int r = (volume_id & 0x000000FF) >> 0;
+ unsigned int g = (volume_id & 0x0000FF00) >> 8;
+ unsigned int b = (volume_id & 0x00FF0000) >> 16;
+ ::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255);
+ }
+ else
+ {
+ vol->set_render_color();
+ ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]);
+ }
+
+ vol->render();
+ ++volume_id;
+ }
+
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisable(GL_BLEND);
+
+ ::glEnable(GL_CULL_FACE);
+}
+
+void GLCanvas3D::_render_gizmo() const
+{
+ m_gizmos.render(*this, _selected_volumes_bounding_box());
+}
+
+float GLCanvas3D::_get_layers_editing_cursor_z_relative() const
+{
+ return m_layers_editing.get_cursor_z_relative(*this);
+}
+
+void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
+{
+ int object_idx_selected = m_layers_editing.last_object_id;
+ if (object_idx_selected == -1)
+ return;
+
+ if (m_print == nullptr)
+ return;
+
+ PrintObject* selected_obj = m_print->get_object(object_idx_selected);
+ if (selected_obj == nullptr)
+ return;
+
+ // A volume is selected. Test, whether hovering over a layer thickness bar.
+ if (evt != nullptr)
+ {
+ const Rect& rect = LayersEditing::get_bar_rect_screen(*this);
+ float b = rect.get_bottom();
+ m_layers_editing.last_z = unscale(selected_obj->size.z) * (b - evt->GetY() - 1.0f) / (b - rect.get_top());
+ m_layers_editing.last_action = evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1);
+ }
+
+ // Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ?
+ // Start a timer to refresh the print ? schedule_background_process() ?
+ // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself.
+ selected_obj->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action);
+
+ // searches the id of the first volume of the selected object
+ int volume_idx = 0;
+ for (int i = 0; i < object_idx_selected; ++i)
+ {
+ PrintObject* obj = m_print->get_object(i);
+ if (obj != nullptr)
+ {
+ for (int j = 0; j < (int)obj->region_volumes.size(); ++j)
+ {
+ volume_idx += (int)obj->region_volumes[j].size();
+ }
+ }
+ }
+
+ m_volumes.volumes[volume_idx]->generate_layer_height_texture(selected_obj, 1);
+ _refresh_if_shown_on_screen();
+
+ // Automatic action on mouse down with the same coordinate.
+ _start_timer();
+}
+
+Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z)
+{
+ if (!set_current())
+ return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX);
+
+ GLint viewport[4];
+ ::glGetIntegerv(GL_VIEWPORT, viewport);
+ GLdouble modelview_matrix[16];
+ ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
+ GLdouble projection_matrix[16];
+ ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
+
+ GLint y = viewport[3] - (GLint)mouse_pos.y;
+ GLfloat mouse_z;
+ if (z == nullptr)
+ ::glReadPixels((GLint)mouse_pos.x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z);
+ else
+ mouse_z = *z;
+
+ GLdouble out_x, out_y, out_z;
+ ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, (GLdouble)mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z);
+ return Pointf3((coordf_t)out_x, (coordf_t)out_y, (coordf_t)out_z);
+}
+
+Pointf3 GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos)
+{
+ float z0 = 0.0f;
+ float z1 = 1.0f;
+ return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1)).intersect_plane(0.0);
+}
+
+void GLCanvas3D::_start_timer()
+{
+ if (m_timer != nullptr)
+ m_timer->Start(100, wxTIMER_CONTINUOUS);
+}
+
+void GLCanvas3D::_stop_timer()
+{
+ if (m_timer != nullptr)
+ m_timer->Stop();
+}
+
+int GLCanvas3D::_get_first_selected_object_id() const
+{
+ if (m_print != nullptr)
+ {
+ int objects_count = (int)m_print->objects.size();
+
+ for (const GLVolume* vol : m_volumes.volumes)
+ {
+ if ((vol != nullptr) && vol->selected)
+ {
+ int object_id = vol->select_group_id / 1000000;
+ // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
+ if (object_id < 10000)
+ return (object_id >= objects_count) ? -1 : object_id;
+ }
+ }
+ }
+ return -1;
+}
+
+static inline int hex_digit_to_int(const char c)
+{
+ return
+ (c >= '0' && c <= '9') ? int(c - '0') :
+ (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
+ (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
+}
+
+void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+{
+ // helper functions to select data in dependence of the extrusion view type
+ struct Helper
+ {
+ static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path)
+ {
+ switch (type)
+ {
+ case GCodePreviewData::Extrusion::FeatureType:
+ return (float)path.role();
+ case GCodePreviewData::Extrusion::Height:
+ return path.height;
+ case GCodePreviewData::Extrusion::Width:
+ return path.width;
+ case GCodePreviewData::Extrusion::Feedrate:
+ return path.feedrate;
+ case GCodePreviewData::Extrusion::VolumetricRate:
+ return path.feedrate * (float)path.mm3_per_mm;
+ case GCodePreviewData::Extrusion::Tool:
+ return (float)path.extruder_id;
+ default:
+ return 0.0f;
+ }
+
+ return 0.0f;
+ }
+
+ static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
+ {
+ switch (data.extrusion.view_type)
+ {
+ case GCodePreviewData::Extrusion::FeatureType:
+ return data.get_extrusion_role_color((ExtrusionRole)(int)value);
+ case GCodePreviewData::Extrusion::Height:
+ return data.get_height_color(value);
+ case GCodePreviewData::Extrusion::Width:
+ return data.get_width_color(value);
+ case GCodePreviewData::Extrusion::Feedrate:
+ return data.get_feedrate_color(value);
+ case GCodePreviewData::Extrusion::VolumetricRate:
+ return data.get_volumetric_rate_color(value);
+ case GCodePreviewData::Extrusion::Tool:
+ {
+ GCodePreviewData::Color color;
+ ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
+ return color;
+ }
+ default:
+ return GCodePreviewData::Color::Dummy;
+ }
+
+ return GCodePreviewData::Color::Dummy;
+ }
+ };
+
+ // Helper structure for filters
+ struct Filter
+ {
+ float value;
+ ExtrusionRole role;
+ GLVolume* volume;
+
+ Filter(float value, ExtrusionRole role)
+ : value(value)
+ , role(role)
+ , volume(nullptr)
+ {
+ }
+
+ bool operator == (const Filter& other) const
+ {
+ if (value != other.value)
+ return false;
+
+ if (role != other.role)
+ return false;
+
+ return true;
+ }
+ };
+
+ typedef std::vector<Filter> FiltersList;
+ size_t initial_volumes_count = m_volumes.volumes.size();
+
+ // detects filters
+ FiltersList filters;
+ for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
+ {
+ for (const ExtrusionPath& path : layer.paths)
+ {
+ ExtrusionRole role = path.role();
+ float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path);
+ if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end())
+ filters.emplace_back(path_filter, role);
+ }
+ }
+
+ // nothing to render, return
+ if (filters.empty())
+ return;
+
+ // creates a new volume for each filter
+ for (Filter& filter : filters)
+ {
+ m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes.volumes.size());
+ GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba);
+ if (volume != nullptr)
+ {
+ filter.volume = volume;
+ m_volumes.volumes.emplace_back(volume);
+ }
+ else
+ {
+ // an error occourred - restore to previous state and return
+ m_gcode_preview_volume_index.first_volumes.pop_back();
+ if (initial_volumes_count != m_volumes.volumes.size())
+ {
+ std::vector<GLVolume*>::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
+ std::vector<GLVolume*>::iterator end = m_volumes.volumes.end();
+ for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it)
+ {
+ GLVolume* volume = *it;
+ delete volume;
+ }
+ m_volumes.volumes.erase(begin, end);
+ return;
+ }
+ }
+ }
+
+ // populates volumes
+ for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
+ {
+ for (const ExtrusionPath& path : layer.paths)
+ {
+ float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path);
+ FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role()));
+ if (filter != filters.end())
+ {
+ filter->volume->print_zs.push_back(layer.z);
+ filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size());
+ filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size());
+
+ _3DScene::extrusionentity_to_verts(path, layer.z, *filter->volume);
+ }
+ }
+ }
+
+ // finalize volumes and sends geometry to gpu
+ if (m_volumes.volumes.size() > initial_volumes_count)
+ {
+ for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
+ {
+ GLVolume* volume = m_volumes.volumes[i];
+ volume->bounding_box = volume->indexed_vertex_array.bounding_box();
+ volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+ }
+ }
+}
+
+void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+{
+ size_t initial_volumes_count = m_volumes.volumes.size();
+ m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count);
+
+ bool res = true;
+ switch (preview_data.extrusion.view_type)
+ {
+ case GCodePreviewData::Extrusion::Feedrate:
+ {
+ res = _travel_paths_by_feedrate(preview_data);
+ break;
+ }
+ case GCodePreviewData::Extrusion::Tool:
+ {
+ res = _travel_paths_by_tool(preview_data, tool_colors);
+ break;
+ }
+ default:
+ {
+ res = _travel_paths_by_type(preview_data);
+ break;
+ }
+ }
+
+ if (!res)
+ {
+ // an error occourred - restore to previous state and return
+ if (initial_volumes_count != m_volumes.volumes.size())
+ {
+ std::vector<GLVolume*>::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
+ std::vector<GLVolume*>::iterator end = m_volumes.volumes.end();
+ for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it)
+ {
+ GLVolume* volume = *it;
+ delete volume;
+ }
+ m_volumes.volumes.erase(begin, end);
+ }
+
+ return;
+ }
+
+ // finalize volumes and sends geometry to gpu
+ if (m_volumes.volumes.size() > initial_volumes_count)
+ {
+ for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
+ {
+ GLVolume* volume = m_volumes.volumes[i];
+ volume->bounding_box = volume->indexed_vertex_array.bounding_box();
+ volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+ }
+ }
+}
+
+bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
+{
+ // Helper structure for types
+ struct Type
+ {
+ GCodePreviewData::Travel::EType value;
+ GLVolume* volume;
+
+ explicit Type(GCodePreviewData::Travel::EType value)
+ : value(value)
+ , volume(nullptr)
+ {
+ }
+
+ bool operator == (const Type& other) const
+ {
+ return value == other.value;
+ }
+ };
+
+ typedef std::vector<Type> TypesList;
+
+ // colors travels by travel type
+
+ // detects types
+ TypesList types;
+ for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
+ {
+ if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end())
+ types.emplace_back(polyline.type);
+ }
+
+ // nothing to render, return
+ if (types.empty())
+ return true;
+
+ // creates a new volume for each type
+ for (Type& type : types)
+ {
+ GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba);
+ if (volume == nullptr)
+ return false;
+ else
+ {
+ type.volume = volume;
+ m_volumes.volumes.emplace_back(volume);
+ }
+ }
+
+ // populates volumes
+ for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
+ {
+ TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type));
+ if (type != types.end())
+ {
+ type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
+ type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size());
+ type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size());
+
+ _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume);
+ }
+ }
+
+ return true;
+}
+
+bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data)
+{
+ // Helper structure for feedrate
+ struct Feedrate
+ {
+ float value;
+ GLVolume* volume;
+
+ explicit Feedrate(float value)
+ : value(value)
+ , volume(nullptr)
+ {
+ }
+
+ bool operator == (const Feedrate& other) const
+ {
+ return value == other.value;
+ }
+ };
+
+ typedef std::vector<Feedrate> FeedratesList;
+
+ // colors travels by feedrate
+
+ // detects feedrates
+ FeedratesList feedrates;
+ for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
+ {
+ if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end())
+ feedrates.emplace_back(polyline.feedrate);
+ }
+
+ // nothing to render, return
+ if (feedrates.empty())
+ return true;
+
+ // creates a new volume for each feedrate
+ for (Feedrate& feedrate : feedrates)
+ {
+ GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba);
+ if (volume == nullptr)
+ return false;
+ else
+ {
+ feedrate.volume = volume;
+ m_volumes.volumes.emplace_back(volume);
+ }
+ }
+
+ // populates volumes
+ for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
+ {
+ FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate));
+ if (feedrate != feedrates.end())
+ {
+ feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
+ feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size());
+ feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size());
+
+ _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume);
+ }
+ }
+
+ return true;
+}
+
+bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+{
+ // Helper structure for tool
+ struct Tool
+ {
+ unsigned int value;
+ GLVolume* volume;
+
+ explicit Tool(unsigned int value)
+ : value(value)
+ , volume(nullptr)
+ {
+ }
+
+ bool operator == (const Tool& other) const
+ {
+ return value == other.value;
+ }
+ };
+
+ typedef std::vector<Tool> ToolsList;
+
+ // colors travels by tool
+
+ // detects tools
+ ToolsList tools;
+ for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
+ {
+ if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end())
+ tools.emplace_back(polyline.extruder_id);
+ }
+
+ // nothing to render, return
+ if (tools.empty())
+ return true;
+
+ // creates a new volume for each tool
+ for (Tool& tool : tools)
+ {
+ GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4);
+ if (volume == nullptr)
+ return false;
+ else
+ {
+ tool.volume = volume;
+ m_volumes.volumes.emplace_back(volume);
+ }
+ }
+
+ // populates volumes
+ for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
+ {
+ ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
+ if (tool != tools.end())
+ {
+ tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
+ tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size());
+ tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size());
+
+ _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume);
+ }
+ }
+
+ return true;
+}
+
+void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data)
+{
+ m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes.volumes.size());
+
+ // nothing to render, return
+ if (preview_data.retraction.positions.empty())
+ return;
+
+ GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba);
+ if (volume != nullptr)
+ {
+ m_volumes.volumes.emplace_back(volume);
+
+ GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions);
+ std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
+
+ for (const GCodePreviewData::Retraction::Position& position : copy)
+ {
+ volume->print_zs.push_back(unscale(position.position.z));
+ volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
+ volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
+
+ _3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
+ }
+
+ // finalize volumes and sends geometry to gpu
+ volume->bounding_box = volume->indexed_vertex_array.bounding_box();
+ volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+ }
+}
+
+void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data)
+{
+ m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes.volumes.size());
+
+ // nothing to render, return
+ if (preview_data.unretraction.positions.empty())
+ return;
+
+ GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba);
+ if (volume != nullptr)
+ {
+ m_volumes.volumes.emplace_back(volume);
+
+ GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions);
+ std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
+
+ for (const GCodePreviewData::Retraction::Position& position : copy)
+ {
+ volume->print_zs.push_back(unscale(position.position.z));
+ volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
+ volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
+
+ _3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
+ }
+
+ // finalize volumes and sends geometry to gpu
+ volume->bounding_box = volume->indexed_vertex_array.bounding_box();
+ volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+ }
+}
+
+void GLCanvas3D::_load_shells()
+{
+ size_t initial_volumes_count = m_volumes.volumes.size();
+ m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count);
+
+ if (m_print->objects.empty())
+ // nothing to render, return
+ return;
+
+ // adds objects' volumes
+ unsigned int object_id = 0;
+ for (PrintObject* obj : m_print->objects)
+ {
+ ModelObject* model_obj = obj->model_object();
+
+ std::vector<int> instance_ids(model_obj->instances.size());
+ for (int i = 0; i < (int)model_obj->instances.size(); ++i)
+ {
+ instance_ids[i] = i;
+ }
+
+ m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized);
+
+ ++object_id;
+ }
+
+ // adds wipe tower's volume
+ coordf_t max_z = m_print->objects[0]->model_object()->get_model()->bounding_box().max.z;
+ const PrintConfig& config = m_print->config;
+ unsigned int extruders_count = config.nozzle_diameter.size();
+ if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
+ const float width_per_extruder = 15.0f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
+ m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized);
+ }
+}
+
+void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data)
+{
+ unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size();
+ for (unsigned int i = 0; i < size; ++i)
+ {
+ std::vector<GLVolume*>::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id;
+ std::vector<GLVolume*>::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end();
+
+ for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
+ {
+ GLVolume* volume = *it;
+ volume->outside_printer_detection_enabled = false;
+
+ switch (m_gcode_preview_volume_index.first_volumes[i].type)
+ {
+ case GCodePreviewVolumeIndex::Extrusion:
+ {
+ if ((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag == erCustom)
+ volume->zoom_to_volumes = false;
+
+ volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag);
+ break;
+ }
+ case GCodePreviewVolumeIndex::Travel:
+ {
+ volume->is_active = preview_data.travel.is_visible;
+ volume->zoom_to_volumes = false;
+ break;
+ }
+ case GCodePreviewVolumeIndex::Retraction:
+ {
+ volume->is_active = preview_data.retraction.is_visible;
+ volume->zoom_to_volumes = false;
+ break;
+ }
+ case GCodePreviewVolumeIndex::Unretraction:
+ {
+ volume->is_active = preview_data.unretraction.is_visible;
+ volume->zoom_to_volumes = false;
+ break;
+ }
+ case GCodePreviewVolumeIndex::Shell:
+ {
+ volume->is_active = preview_data.shell.is_visible;
+ volume->color[3] = 0.25f;
+ volume->zoom_to_volumes = false;
+ break;
+ }
+ default:
+ {
+ volume->is_active = false;
+ volume->zoom_to_volumes = false;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
+{
+ if (m_model == nullptr)
+ return;
+
+ std::set<std::string> done; // prevent moving instances twice
+ bool object_moved = false;
+ Pointf3 wipe_tower_origin(0.0, 0.0, 0.0);
+ for (int volume_idx : volume_idxs)
+ {
+ GLVolume* volume = m_volumes.volumes[volume_idx];
+ int obj_idx = volume->object_idx();
+ int instance_idx = volume->instance_idx();
+
+ // prevent moving instances twice
+ char done_id[64];
+ ::sprintf(done_id, "%d_%d", obj_idx, instance_idx);
+ if (done.find(done_id) != done.end())
+ continue;
+
+ done.insert(done_id);
+
+ if (obj_idx < 1000)
+ {
+ // Move a regular object.
+ ModelObject* model_object = m_model->objects[obj_idx];
+ model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y);
+ model_object->invalidate_bounding_box();
+ object_moved = true;
+ }
+ else if (obj_idx == 1000)
+ // Move a wipe tower proxy.
+ wipe_tower_origin = volume->origin;
+ }
+
+ if (object_moved)
+ m_on_instance_moved_callback.call();
+
+ if (wipe_tower_origin != Pointf3(0.0, 0.0, 0.0))
+ m_on_wipe_tower_moved_callback.call(wipe_tower_origin.x, wipe_tower_origin.y);
+}
+
+void GLCanvas3D::_on_select(int volume_idx)
+{
+ int id = -1;
+ if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size()))
+ {
+ if (m_select_by == "volume")
+ id = m_volumes.volumes[volume_idx]->volume_idx();
+ else if (m_select_by == "object")
+ id = m_volumes.volumes[volume_idx]->object_idx();
+ }
+ m_on_select_object_callback.call(id);
+}
+
+void GLCanvas3D::_update_gizmos_data()
+{
+ int id = _get_first_selected_object_id();
+ if ((id != -1) && (m_model != nullptr))
+ {
+ ModelObject* model_object = m_model->objects[id];
+ if (model_object != nullptr)
+ {
+ ModelInstance* model_instance = model_object->instances[0];
+ if (model_instance != nullptr)
+ m_gizmos.update_data(model_instance->scaling_factor);
+ }
+ }
+}
+
+std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
+{
+ static const float INV_255 = 1.0f / 255.0f;
+
+ std::vector<float> output(colors.size() * 4, 1.0f);
+ for (size_t i = 0; i < colors.size(); ++i)
+ {
+ const std::string& color = colors[i];
+ const char* c = color.data() + 1;
+ if ((color.size() == 7) && (color.front() == '#'))
+ {
+ for (size_t j = 0; j < 3; ++j)
+ {
+ int digit1 = hex_digit_to_int(*c++);
+ int digit2 = hex_digit_to_int(*c++);
+ if ((digit1 == -1) || (digit2 == -1))
+ break;
+
+ output[i * 4 + j] = float(digit1 * 16 + digit2) * INV_255;
+ }
+ }
+ }
+ return output;
+}
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
new file mode 100644
index 000000000..c503d1845
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -0,0 +1,639 @@
+#ifndef slic3r_GLCanvas3D_hpp_
+#define slic3r_GLCanvas3D_hpp_
+
+#include "../../slic3r/GUI/3DScene.hpp"
+#include "../../slic3r/GUI/GLTexture.hpp"
+
+class wxTimer;
+class wxSizeEvent;
+class wxIdleEvent;
+class wxKeyEvent;
+class wxMouseEvent;
+class wxTimerEvent;
+class wxPaintEvent;
+
+namespace Slic3r {
+
+class GLShader;
+class ExPolygon;
+
+namespace GUI {
+
+class GLGizmoBase;
+
+class GeometryBuffer
+{
+ std::vector<float> m_vertices;
+ std::vector<float> m_tex_coords;
+
+public:
+ bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
+ bool set_from_lines(const Lines& lines, float z);
+
+ const float* get_vertices() const;
+ const float* get_tex_coords() const;
+
+ unsigned int get_vertices_count() const;
+};
+
+class Size
+{
+ int m_width;
+ int m_height;
+
+public:
+ Size();
+ Size(int width, int height);
+
+ int get_width() const;
+ void set_width(int width);
+
+ int get_height() const;
+ void set_height(int height);
+};
+
+class Rect
+{
+ float m_left;
+ float m_top;
+ float m_right;
+ float m_bottom;
+
+public:
+ Rect();
+ Rect(float left, float top, float right, float bottom);
+
+ float get_left() const;
+ void set_left(float left);
+
+ float get_top() const;
+ void set_top(float top);
+
+ float get_right() const;
+ void set_right(float right);
+
+ float get_bottom() const;
+ void set_bottom(float bottom);
+};
+
+class GLCanvas3D
+{
+ struct GCodePreviewVolumeIndex
+ {
+ enum EType
+ {
+ Extrusion,
+ Travel,
+ Retraction,
+ Unretraction,
+ Shell,
+ Num_Geometry_Types
+ };
+
+ struct FirstVolume
+ {
+ EType type;
+ unsigned int flag;
+ // Index of the first volume in a GLVolumeCollection.
+ unsigned int id;
+
+ FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {}
+ };
+
+ std::vector<FirstVolume> first_volumes;
+
+ void reset() { first_volumes.clear(); }
+ };
+
+public:
+ struct Camera
+ {
+ enum EType : unsigned char
+ {
+ Unknown,
+// Perspective,
+ Ortho,
+ Num_types
+ };
+
+ EType type;
+ float zoom;
+ float phi;
+// float distance;
+ Pointf3 target;
+
+ private:
+ float m_theta;
+
+ public:
+ Camera();
+
+ std::string get_type_as_string() const;
+
+ float get_theta() const;
+ void set_theta(float theta);
+ };
+
+ class Bed
+ {
+ public:
+ enum EType : unsigned char
+ {
+ MK2,
+ MK3,
+ Custom,
+ Num_Types
+ };
+
+ private:
+ EType m_type;
+ Pointfs m_shape;
+ BoundingBoxf3 m_bounding_box;
+ Polygon m_polygon;
+ GeometryBuffer m_triangles;
+ GeometryBuffer m_gridlines;
+ mutable GLTexture m_top_texture;
+ mutable GLTexture m_bottom_texture;
+
+ public:
+ Bed();
+
+ bool is_prusa() const;
+ bool is_custom() const;
+
+ const Pointfs& get_shape() const;
+ void set_shape(const Pointfs& shape);
+
+ const BoundingBoxf3& get_bounding_box() const;
+ bool contains(const Point& point) const;
+ Point point_projection(const Point& point) const;
+
+ void render(float theta) const;
+
+ private:
+ void _calc_bounding_box();
+ void _calc_triangles(const ExPolygon& poly);
+ void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
+ EType _detect_type() const;
+ void _render_mk2(float theta) const;
+ void _render_mk3(float theta) const;
+ void _render_prusa(float theta) const;
+ void _render_custom() const;
+ static bool _are_equal(const Pointfs& bed_1, const Pointfs& bed_2);
+ };
+
+ struct Axes
+ {
+ Pointf3 origin;
+ float length;
+
+ Axes();
+
+ void render(bool depth_test) const;
+ };
+
+ class CuttingPlane
+ {
+ float m_z;
+ GeometryBuffer m_lines;
+
+ public:
+ CuttingPlane();
+
+ bool set(float z, const ExPolygons& polygons);
+
+ void render(const BoundingBoxf3& bb) const;
+
+ private:
+ void _render_plane(const BoundingBoxf3& bb) const;
+ void _render_contour() const;
+ };
+
+ class Shader
+ {
+ GLShader* m_shader;
+
+ public:
+ Shader();
+ ~Shader();
+
+ bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
+
+ bool is_initialized() const;
+
+ bool start_using() const;
+ void stop_using() const;
+
+ void set_uniform(const std::string& name, float value) const;
+
+ const GLShader* get_shader() const;
+
+ private:
+ void _reset();
+ };
+
+ class LayersEditing
+ {
+ public:
+ enum EState : unsigned char
+ {
+ Unknown,
+ Editing,
+ Completed,
+ Num_States
+ };
+
+ private:
+ bool m_use_legacy_opengl;
+ bool m_enabled;
+ Shader m_shader;
+ unsigned int m_z_texture_id;
+ mutable GLTexture m_tooltip_texture;
+ mutable GLTexture m_reset_texture;
+
+ public:
+ EState state;
+ float band_width;
+ float strength;
+ int last_object_id;
+ float last_z;
+ unsigned int last_action;
+
+ LayersEditing();
+ ~LayersEditing();
+
+ bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
+
+ bool is_allowed() const;
+ void set_use_legacy_opengl(bool use_legacy_opengl);
+
+ bool is_enabled() const;
+ void set_enabled(bool enabled);
+
+ unsigned int get_z_texture_id() const;
+
+ void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const;
+
+ int get_shader_program_id() const;
+
+ static float get_cursor_z_relative(const GLCanvas3D& canvas);
+ static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
+ static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y);
+ static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
+ static Rect get_reset_rect_screen(const GLCanvas3D& canvas);
+ static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
+ static Rect get_reset_rect_viewport(const GLCanvas3D& canvas);
+
+ private:
+ bool _is_initialized() const;
+ void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const;
+ void _render_reset_texture(const Rect& reset_rect) const;
+ void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const;
+ void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const;
+ };
+
+ struct Mouse
+ {
+ struct Drag
+ {
+ static const Point Invalid_2D_Point;
+ static const Pointf3 Invalid_3D_Point;
+
+ Point start_position_2D;
+ Pointf3 start_position_3D;
+ Vectorf3 volume_center_offset;
+ int volume_idx;
+
+ public:
+ Drag();
+ };
+
+ bool dragging;
+ Pointf position;
+ Drag drag;
+
+ Mouse();
+
+ void set_start_position_2D_as_invalid();
+ void set_start_position_3D_as_invalid();
+
+ bool is_start_position_2D_defined() const;
+ bool is_start_position_3D_defined() const;
+ };
+
+ class Gizmos
+ {
+ static const float OverlayOffsetX;
+ static const float OverlayGapY;
+
+ public:
+ enum EType : unsigned char
+ {
+ Undefined,
+ Scale,
+ Rotate,
+ Num_Types
+ };
+
+ private:
+ bool m_enabled;
+ typedef std::map<EType, GLGizmoBase*> GizmosMap;
+ GizmosMap m_gizmos;
+ EType m_current;
+ bool m_dragging;
+
+ public:
+ Gizmos();
+ ~Gizmos();
+
+ bool init();
+
+ bool is_enabled() const;
+ void set_enabled(bool enable);
+
+ void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos);
+ void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos);
+ void reset_all_states();
+
+ void set_hover_id(int id);
+
+ bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const;
+ bool grabber_contains_mouse() const;
+ void update(const Pointf& mouse_pos);
+ void update_data(float scale);
+
+ bool is_running() const;
+ bool is_dragging() const;
+ void start_dragging();
+ void stop_dragging();
+
+ float get_scale() const;
+
+ void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
+ void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
+
+ private:
+ void _reset();
+
+ void _render_overlay(const GLCanvas3D& canvas) const;
+ void _render_current_gizmo(const BoundingBoxf3& box) const;
+
+ float _get_total_overlay_height() const;
+ GLGizmoBase* _get_current() const;
+ };
+
+private:
+ wxGLCanvas* m_canvas;
+ wxGLContext* m_context;
+ wxTimer* m_timer;
+ Camera m_camera;
+ Bed m_bed;
+ Axes m_axes;
+ CuttingPlane m_cutting_plane;
+ LayersEditing m_layers_editing;
+ Shader m_shader;
+ Mouse m_mouse;
+ mutable Gizmos m_gizmos;
+
+ mutable GLVolumeCollection m_volumes;
+ DynamicPrintConfig* m_config;
+ Print* m_print;
+ Model* m_model;
+
+ bool m_dirty;
+ // the active member has been introduced to overcome a bug in wxWidgets method IsShownOnScreen() which always return true
+ // when a window is inside a wxNotebook
+ bool m_active;
+ bool m_initialized;
+ bool m_use_VBOs;
+ bool m_force_zoom_to_bed_enabled;
+ bool m_apply_zoom_to_volumes_filter;
+ mutable int m_hover_volume_id;
+ bool m_warning_texture_enabled;
+ bool m_legend_texture_enabled;
+ bool m_picking_enabled;
+ bool m_moving_enabled;
+ bool m_shader_enabled;
+ bool m_multisample_allowed;
+
+ std::string m_color_by;
+ std::string m_select_by;
+ std::string m_drag_by;
+
+ bool m_reload_delayed;
+ std::vector<std::vector<int>> m_objects_volumes_idxs;
+ std::vector<int> m_objects_selections;
+
+ GCodePreviewVolumeIndex m_gcode_preview_volume_index;
+
+ PerlCallback m_on_viewport_changed_callback;
+ PerlCallback m_on_double_click_callback;
+ PerlCallback m_on_right_click_callback;
+ PerlCallback m_on_select_object_callback;
+ PerlCallback m_on_model_update_callback;
+ PerlCallback m_on_remove_object_callback;
+ PerlCallback m_on_arrange_callback;
+ PerlCallback m_on_rotate_object_left_callback;
+ PerlCallback m_on_rotate_object_right_callback;
+ PerlCallback m_on_scale_object_uniformly_callback;
+ PerlCallback m_on_increase_objects_callback;
+ PerlCallback m_on_decrease_objects_callback;
+ PerlCallback m_on_instance_moved_callback;
+ PerlCallback m_on_wipe_tower_moved_callback;
+ PerlCallback m_on_enable_action_buttons_callback;
+ PerlCallback m_on_gizmo_scale_uniformly_callback;
+
+public:
+ GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context);
+ ~GLCanvas3D();
+
+ bool init(bool useVBOs, bool use_legacy_opengl);
+
+ bool set_current();
+
+ void set_active(bool active);
+
+ unsigned int get_volumes_count() const;
+ void reset_volumes();
+ void deselect_volumes();
+ void select_volume(unsigned int id);
+ void update_volumes_selection(const std::vector<int>& selections);
+ bool check_volumes_outside_state(const DynamicPrintConfig* config) const;
+ bool move_volume_up(unsigned int id);
+ bool move_volume_down(unsigned int id);
+
+ void set_objects_selections(const std::vector<int>& selections);
+
+ void set_config(DynamicPrintConfig* config);
+ void set_print(Print* print);
+ void set_model(Model* model);
+
+ // Set the bed shape to a single closed 2D polygon(array of two element arrays),
+ // triangulate the bed and store the triangles into m_bed.m_triangles,
+ // fills the m_bed.m_grid_lines and sets m_bed.m_origin.
+ // Sets m_bed.m_polygon to limit the object placement.
+ void set_bed_shape(const Pointfs& shape);
+ // Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane to support the scene objects.
+ void set_auto_bed_shape();
+
+ void set_axes_length(float length);
+
+ void set_cutting_plane(float z, const ExPolygons& polygons);
+
+ void set_color_by(const std::string& value);
+ void set_select_by(const std::string& value);
+ void set_drag_by(const std::string& value);
+
+ float get_camera_zoom() const;
+
+ BoundingBoxf3 volumes_bounding_box() const;
+
+ bool is_layers_editing_enabled() const;
+ bool is_layers_editing_allowed() const;
+ bool is_shader_enabled() const;
+
+ bool is_reload_delayed() const;
+
+ void enable_layers_editing(bool enable);
+ void enable_warning_texture(bool enable);
+ void enable_legend_texture(bool enable);
+ void enable_picking(bool enable);
+ void enable_moving(bool enable);
+ void enable_gizmos(bool enable);
+ void enable_shader(bool enable);
+ void enable_force_zoom_to_bed(bool enable);
+ void allow_multisample(bool allow);
+
+ void zoom_to_bed();
+ void zoom_to_volumes();
+ void select_view(const std::string& direction);
+ void set_viewport_from_scene(const GLCanvas3D& other);
+
+ void update_volumes_colors_by_extruder();
+
+ void render();
+
+ std::vector<double> get_current_print_zs(bool active_only) const;
+ void set_toolpaths_range(double low, double high);
+
+ std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
+ std::vector<int> load_object(const Model& model, int obj_idx);
+
+ void reload_scene(bool force);
+
+ // Create 3D thick extrusion lines for a skirt and brim.
+ // Adds a new Slic3r::GUI::3DScene::Volume to volumes.
+ void load_print_toolpaths();
+ // Create 3D thick extrusion lines for object forming extrusions.
+ // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
+ // one for perimeters, one for infill and one for supports.
+ void load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
+ // Create 3D thick extrusion lines for wipe tower extrusions
+ void load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
+ void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
+
+ void register_on_viewport_changed_callback(void* callback);
+ void register_on_double_click_callback(void* callback);
+ void register_on_right_click_callback(void* callback);
+ void register_on_select_object_callback(void* callback);
+ void register_on_model_update_callback(void* callback);
+ void register_on_remove_object_callback(void* callback);
+ void register_on_arrange_callback(void* callback);
+ void register_on_rotate_object_left_callback(void* callback);
+ void register_on_rotate_object_right_callback(void* callback);
+ void register_on_scale_object_uniformly_callback(void* callback);
+ void register_on_increase_objects_callback(void* callback);
+ void register_on_decrease_objects_callback(void* callback);
+ void register_on_instance_moved_callback(void* callback);
+ void register_on_wipe_tower_moved_callback(void* callback);
+ void register_on_enable_action_buttons_callback(void* callback);
+ void register_on_gizmo_scale_uniformly_callback(void* callback);
+
+ void bind_event_handlers();
+ void unbind_event_handlers();
+
+ void on_size(wxSizeEvent& evt);
+ void on_idle(wxIdleEvent& evt);
+ void on_char(wxKeyEvent& evt);
+ void on_mouse_wheel(wxMouseEvent& evt);
+ void on_timer(wxTimerEvent& evt);
+ void on_mouse(wxMouseEvent& evt);
+ void on_paint(wxPaintEvent& evt);
+ void on_key_down(wxKeyEvent& evt);
+
+ Size get_canvas_size() const;
+ Point get_local_mouse_position() const;
+
+private:
+ bool _is_shown_on_screen() const;
+ void _force_zoom_to_bed();
+
+ void _resize(unsigned int w, unsigned int h);
+
+ BoundingBoxf3 _max_bounding_box() const;
+ BoundingBoxf3 _selected_volumes_bounding_box() const;
+
+ void _zoom_to_bounding_box(const BoundingBoxf3& bbox);
+ float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const;
+
+ void _deregister_callbacks();
+
+ void _mark_volumes_for_layer_height() const;
+ void _refresh_if_shown_on_screen();
+
+ void _camera_tranform() const;
+ void _picking_pass() const;
+ void _render_background() const;
+ void _render_bed(float theta) const;
+ void _render_axes(bool depth_test) const;
+ void _render_objects() const;
+ void _render_cutting_plane() const;
+ void _render_warning_texture() const;
+ void _render_legend_texture() const;
+ void _render_layer_editing_overlay() const;
+ void _render_volumes(bool fake_colors) const;
+ void _render_gizmo() const;
+
+ float _get_layers_editing_cursor_z_relative() const;
+ void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
+
+ // Convert the screen space coordinate to an object space coordinate.
+ // If the Z screen space coordinate is not provided, a depth buffer value is substituted.
+ Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr);
+
+ // Convert the screen space coordinate to world coordinate on the bed.
+ Pointf3 _mouse_to_bed_3d(const Point& mouse_pos);
+
+ void _start_timer();
+ void _stop_timer();
+
+ int _get_first_selected_object_id() const;
+
+ // generates gcode extrusion paths geometry
+ void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
+ // generates gcode travel paths geometry
+ void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
+ bool _travel_paths_by_type(const GCodePreviewData& preview_data);
+ bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data);
+ bool _travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
+ // generates gcode retractions geometry
+ void _load_gcode_retractions(const GCodePreviewData& preview_data);
+ // generates gcode unretractions geometry
+ void _load_gcode_unretractions(const GCodePreviewData& preview_data);
+ // generates objects and wipe tower geometry
+ void _load_shells();
+ // sets gcode geometry visibility according to user selection
+ void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
+
+ void _on_move(const std::vector<int>& volume_idxs);
+ void _on_select(int volume_idx);
+
+ void _update_gizmos_data();
+
+ static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
+};
+
+} // namespace GUI
+} // namespace Slic3r
+
+#endif // slic3r_GLCanvas3D_hpp_
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
new file mode 100644
index 000000000..f288ee456
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -0,0 +1,707 @@
+#include "GLCanvas3DManager.hpp"
+#include "../../slic3r/GUI/GUI.hpp"
+#include "../../slic3r/GUI/AppConfig.hpp"
+#include "../../slic3r/GUI/GLCanvas3D.hpp"
+
+#include <GL/glew.h>
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+
+#include <wx/glcanvas.h>
+#include <wx/timer.h>
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+namespace Slic3r {
+namespace GUI {
+
+GLCanvas3DManager::GLInfo::GLInfo()
+ : version("")
+ , glsl_version("")
+ , vendor("")
+ , renderer("")
+{
+}
+
+bool GLCanvas3DManager::GLInfo::detect()
+{
+ const char* data = (const char*)::glGetString(GL_VERSION);
+ if (data == nullptr)
+ return false;
+
+ version = data;
+
+ data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION);
+ if (data == nullptr)
+ return false;
+
+ glsl_version = data;
+
+ data = (const char*)::glGetString(GL_VENDOR);
+ if (data == nullptr)
+ return false;
+
+ vendor = data;
+
+ data = (const char*)::glGetString(GL_RENDERER);
+ if (data == nullptr)
+ return false;
+
+ renderer = data;
+
+ return true;
+}
+
+bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
+{
+ std::vector<std::string> tokens;
+ boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
+
+ if (tokens.empty())
+ return false;
+
+ std::vector<std::string> numbers;
+ boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on);
+
+ unsigned int gl_major = 0;
+ unsigned int gl_minor = 0;
+
+ if (numbers.size() > 0)
+ gl_major = ::atoi(numbers[0].c_str());
+
+ if (numbers.size() > 1)
+ gl_minor = ::atoi(numbers[1].c_str());
+
+ if (gl_major < major)
+ return false;
+ else if (gl_major > major)
+ return true;
+ else
+ return gl_minor >= minor;
+}
+
+std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const
+{
+ std::stringstream out;
+
+ std::string h2_start = format_as_html ? "<b>" : "";
+ std::string h2_end = format_as_html ? "</b>" : "";
+ std::string b_start = format_as_html ? "<b>" : "";
+ std::string b_end = format_as_html ? "</b>" : "";
+ std::string line_end = format_as_html ? "<br>" : "\n";
+
+ out << h2_start << "OpenGL installation" << h2_end << line_end;
+ out << b_start << "GL version: " << b_end << version << line_end;
+ out << b_start << "Vendor: " << b_end << vendor << line_end;
+ out << b_start << "Renderer: " << b_end << renderer << line_end;
+ out << b_start << "GLSL version: " << b_end << glsl_version << line_end;
+
+ if (extensions)
+ {
+ out << h2_start << "Installed extensions:" << h2_end << line_end;
+
+ std::vector<std::string> extensions_list;
+ GLint num_extensions;
+ ::glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
+
+ for (GLint i = 0; i < num_extensions; ++i)
+ {
+ const char* e = (const char*)::glGetStringi(GL_EXTENSIONS, i);
+ extensions_list.push_back(e);
+ }
+
+ std::sort(extensions_list.begin(), extensions_list.end());
+ for (const std::string& ext : extensions_list)
+ {
+ out << ext << line_end;
+ }
+ }
+
+ return out.str();
+}
+
+GLCanvas3DManager::GLCanvas3DManager()
+ : m_context(nullptr)
+ , m_gl_initialized(false)
+ , m_use_legacy_opengl(false)
+ , m_use_VBOs(false)
+{
+}
+
+GLCanvas3DManager::~GLCanvas3DManager()
+{
+ if (m_context != nullptr)
+ delete m_context;
+}
+
+bool GLCanvas3DManager::add(wxGLCanvas* canvas)
+{
+ if (canvas == nullptr)
+ return false;
+
+ if (_get_canvas(canvas) != m_canvases.end())
+ return false;
+
+ if (m_context == nullptr)
+ {
+ m_context = new wxGLContext(canvas);
+ if (m_context == nullptr)
+ return false;
+ }
+
+ GLCanvas3D* canvas3D = new GLCanvas3D(canvas, m_context);
+ if (canvas3D == nullptr)
+ return false;
+
+ canvas3D->bind_event_handlers();
+ m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D));
+
+ return true;
+}
+
+bool GLCanvas3DManager::remove(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it == m_canvases.end())
+ return false;
+
+ it->second->unbind_event_handlers();
+ delete it->second;
+ m_canvases.erase(it);
+
+ return true;
+}
+
+void GLCanvas3DManager::remove_all()
+{
+ for (CanvasesMap::value_type& item : m_canvases)
+ {
+ item.second->unbind_event_handlers();
+ delete item.second;
+ }
+ m_canvases.clear();
+}
+
+unsigned int GLCanvas3DManager::count() const
+{
+ return (unsigned int)m_canvases.size();
+}
+
+void GLCanvas3DManager::init_gl()
+{
+ if (!m_gl_initialized)
+ {
+ glewInit();
+ if (m_gl_info.detect())
+ {
+ const AppConfig* config = GUI::get_app_config();
+ m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1");
+ m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0);
+ m_gl_initialized = true;
+ }
+ else
+ throw std::runtime_error(std::string("Unable to initialize OpenGL driver\n"));
+ }
+}
+
+std::string GLCanvas3DManager::get_gl_info(bool format_as_html, bool extensions) const
+{
+ return m_gl_info.to_string(format_as_html, extensions);
+}
+
+bool GLCanvas3DManager::use_VBOs() const
+{
+ return m_use_VBOs;
+}
+
+bool GLCanvas3DManager::init(wxGLCanvas* canvas)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ return (it->second != nullptr) ? _init(*it->second) : false;
+ else
+ return false;
+}
+
+void GLCanvas3DManager::set_active(wxGLCanvas* canvas, bool active)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_active(active);
+}
+
+unsigned int GLCanvas3DManager::get_volumes_count(wxGLCanvas* canvas) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->get_volumes_count() : 0;
+}
+
+void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->reset_volumes();
+}
+
+void GLCanvas3DManager::deselect_volumes(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->deselect_volumes();
+}
+
+void GLCanvas3DManager::select_volume(wxGLCanvas* canvas, unsigned int id)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->select_volume(id);
+}
+
+void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->update_volumes_selection(selections);
+}
+
+bool GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false;
+}
+
+bool GLCanvas3DManager::move_volume_up(wxGLCanvas* canvas, unsigned int id)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->move_volume_up(id) : false;
+}
+
+bool GLCanvas3DManager::move_volume_down(wxGLCanvas* canvas, unsigned int id)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->move_volume_down(id) : false;
+}
+
+void GLCanvas3DManager::set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_objects_selections(selections);
+}
+
+void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_config(config);
+}
+
+void GLCanvas3DManager::set_print(wxGLCanvas* canvas, Print* print)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_print(print);
+}
+
+void GLCanvas3DManager::set_model(wxGLCanvas* canvas, Model* model)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_model(model);
+}
+
+void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_bed_shape(shape);
+}
+
+void GLCanvas3DManager::set_auto_bed_shape(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_auto_bed_shape();
+}
+
+BoundingBoxf3 GLCanvas3DManager::get_volumes_bounding_box(wxGLCanvas* canvas)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->volumes_bounding_box() : BoundingBoxf3();
+}
+
+void GLCanvas3DManager::set_axes_length(wxGLCanvas* canvas, float length)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_axes_length(length);
+}
+
+void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_cutting_plane(z, polygons);
+}
+
+void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& value)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_color_by(value);
+}
+
+void GLCanvas3DManager::set_select_by(wxGLCanvas* canvas, const std::string& value)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_select_by(value);
+}
+
+void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_drag_by(value);
+}
+
+bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->is_layers_editing_enabled() : false;
+}
+
+bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->is_layers_editing_allowed() : false;
+}
+
+bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false;
+}
+
+bool GLCanvas3DManager::is_reload_delayed(wxGLCanvas* canvas) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->is_reload_delayed() : false;
+}
+
+void GLCanvas3DManager::enable_layers_editing(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_layers_editing(enable);
+}
+
+void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_warning_texture(enable);
+}
+
+void GLCanvas3DManager::enable_legend_texture(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_legend_texture(enable);
+}
+
+void GLCanvas3DManager::enable_picking(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_picking(enable);
+}
+
+void GLCanvas3DManager::enable_moving(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_moving(enable);
+}
+
+void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_gizmos(enable);
+}
+
+void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_shader(enable);
+}
+
+void GLCanvas3DManager::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_force_zoom_to_bed(enable);
+}
+
+void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->allow_multisample(allow);
+}
+
+void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->zoom_to_bed();
+}
+
+void GLCanvas3DManager::zoom_to_volumes(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->zoom_to_volumes();
+}
+
+void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direction)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->select_view(direction);
+}
+
+void GLCanvas3DManager::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ {
+ CanvasesMap::iterator other_it = _get_canvas(other);
+ if (other_it != m_canvases.end())
+ it->second->set_viewport_from_scene(*other_it->second);
+ }
+}
+
+void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->update_volumes_colors_by_extruder();
+}
+
+void GLCanvas3DManager::render(wxGLCanvas* canvas) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->render();
+}
+
+std::vector<double> GLCanvas3DManager::get_current_print_zs(wxGLCanvas* canvas, bool active_only) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->get_current_print_zs(active_only) : std::vector<double>();
+}
+
+void GLCanvas3DManager::set_toolpaths_range(wxGLCanvas* canvas, double low, double high)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->set_toolpaths_range(low, high);
+}
+
+std::vector<int> GLCanvas3DManager::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs)
+{
+ if (model_object == nullptr)
+ return std::vector<int>();
+
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->load_object(*model_object, obj_idx, instance_idxs) : std::vector<int>();
+}
+
+std::vector<int> GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx)
+{
+ if (model == nullptr)
+ return std::vector<int>();
+
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector<int>();
+}
+
+void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->reload_scene(force);
+}
+
+void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->load_print_toolpaths();
+}
+
+void GLCanvas3DManager::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors)
+{
+ if (print_object == nullptr)
+ return;
+
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->load_print_object_toolpaths(*print_object, tool_colors);
+}
+
+void GLCanvas3DManager::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->load_wipe_tower_toolpaths(str_tool_colors);
+}
+
+void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
+{
+ if (preview_data == nullptr)
+ return;
+
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->load_gcode_preview(*preview_data, str_tool_colors);
+}
+
+void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_viewport_changed_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_double_click_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_double_click_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_right_click_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_right_click_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_select_object_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_select_object_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_model_update_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_model_update_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_remove_object_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_arrange_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_arrange_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_rotate_object_left_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_rotate_object_right_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_scale_object_uniformly_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_increase_objects_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_decrease_objects_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_instance_moved_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_wipe_tower_moved_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_enable_action_buttons_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_gizmo_scale_uniformly_callback(callback);
+}
+
+GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
+{
+ return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
+}
+
+GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) const
+{
+ return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
+}
+
+bool GLCanvas3DManager::_init(GLCanvas3D& canvas)
+{
+ if (!m_gl_initialized)
+ init_gl();
+
+ return canvas.init(m_use_VBOs, m_use_legacy_opengl);
+}
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
new file mode 100644
index 000000000..6989da791
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -0,0 +1,167 @@
+#ifndef slic3r_GLCanvas3DManager_hpp_
+#define slic3r_GLCanvas3DManager_hpp_
+
+#include "../../libslic3r/BoundingBox.hpp"
+
+#include <map>
+#include <vector>
+
+class wxGLCanvas;
+class wxGLContext;
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+class Print;
+class Model;
+class ExPolygon;
+typedef std::vector<ExPolygon> ExPolygons;
+class ModelObject;
+class PrintObject;
+class GCodePreviewData;
+
+namespace GUI {
+
+class GLCanvas3D;
+
+class GLCanvas3DManager
+{
+ struct GLInfo
+ {
+ std::string version;
+ std::string glsl_version;
+ std::string vendor;
+ std::string renderer;
+
+ GLInfo();
+
+ bool detect();
+ bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
+
+ std::string to_string(bool format_as_html, bool extensions) const;
+ };
+
+ typedef std::map<wxGLCanvas*, GLCanvas3D*> CanvasesMap;
+
+ wxGLContext* m_context;
+ CanvasesMap m_canvases;
+ GLInfo m_gl_info;
+ bool m_gl_initialized;
+ bool m_use_legacy_opengl;
+ bool m_use_VBOs;
+
+public:
+ GLCanvas3DManager();
+ ~GLCanvas3DManager();
+
+ bool add(wxGLCanvas* canvas);
+ bool remove(wxGLCanvas* canvas);
+
+ void remove_all();
+
+ unsigned int count() const;
+
+ void init_gl();
+ std::string get_gl_info(bool format_as_html, bool extensions) const;
+
+ bool use_VBOs() const;
+ bool layer_editing_allowed() const;
+
+ bool init(wxGLCanvas* canvas);
+
+ void set_active(wxGLCanvas* canvas, bool active);
+
+ unsigned int get_volumes_count(wxGLCanvas* canvas) const;
+ void reset_volumes(wxGLCanvas* canvas);
+ void deselect_volumes(wxGLCanvas* canvas);
+ void select_volume(wxGLCanvas* canvas, unsigned int id);
+ void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
+ bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
+ bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
+ bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
+
+ void set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections);
+
+ void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config);
+ void set_print(wxGLCanvas* canvas, Print* print);
+ void set_model(wxGLCanvas* canvas, Model* model);
+
+ void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape);
+ void set_auto_bed_shape(wxGLCanvas* canvas);
+
+ BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas);
+
+ void set_axes_length(wxGLCanvas* canvas, float length);
+
+ void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons);
+
+ void set_color_by(wxGLCanvas* canvas, const std::string& value);
+ void set_select_by(wxGLCanvas* canvas, const std::string& value);
+ void set_drag_by(wxGLCanvas* canvas, const std::string& value);
+
+ bool is_layers_editing_enabled(wxGLCanvas* canvas) const;
+ bool is_layers_editing_allowed(wxGLCanvas* canvas) const;
+ bool is_shader_enabled(wxGLCanvas* canvas) const;
+
+ bool is_reload_delayed(wxGLCanvas* canvas) const;
+
+ void enable_layers_editing(wxGLCanvas* canvas, bool enable);
+ void enable_warning_texture(wxGLCanvas* canvas, bool enable);
+ void enable_legend_texture(wxGLCanvas* canvas, bool enable);
+ void enable_picking(wxGLCanvas* canvas, bool enable);
+ void enable_moving(wxGLCanvas* canvas, bool enable);
+ void enable_gizmos(wxGLCanvas* canvas, bool enable);
+ void enable_shader(wxGLCanvas* canvas, bool enable);
+ void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
+ void allow_multisample(wxGLCanvas* canvas, bool allow);
+
+ void zoom_to_bed(wxGLCanvas* canvas);
+ void zoom_to_volumes(wxGLCanvas* canvas);
+ void select_view(wxGLCanvas* canvas, const std::string& direction);
+ void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
+
+ void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
+
+ void render(wxGLCanvas* canvas) const;
+
+ std::vector<double> get_current_print_zs(wxGLCanvas* canvas, bool active_only) const;
+ void set_toolpaths_range(wxGLCanvas* canvas, double low, double high);
+
+ std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
+ std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
+
+ void reload_scene(wxGLCanvas* canvas, bool force);
+
+ void load_print_toolpaths(wxGLCanvas* canvas);
+ void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors);
+ void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
+ void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
+
+ void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_double_click_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_right_click_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_select_object_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_model_update_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_arrange_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
+
+private:
+ CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
+ CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const;
+
+ bool _init(GLCanvas3D& canvas);
+};
+
+} // namespace GUI
+} // namespace Slic3r
+
+#endif // slic3r_GLCanvas3DManager_hpp_
diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
new file mode 100644
index 000000000..d3aae33e8
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLGizmo.cpp
@@ -0,0 +1,454 @@
+#include "GLGizmo.hpp"
+
+#include "../../libslic3r/Utils.hpp"
+#include "../../libslic3r/BoundingBox.hpp"
+
+#include <GL/glew.h>
+
+#include <iostream>
+
+namespace Slic3r {
+namespace GUI {
+
+const float GLGizmoBase::Grabber::HalfSize = 2.0f;
+const float GLGizmoBase::Grabber::HoverOffset = 0.5f;
+const float GLGizmoBase::BaseColor[3] = { 1.0f, 1.0f, 1.0f };
+const float GLGizmoBase::HighlightColor[3] = { 1.0f, 0.38f, 0.0f };
+
+GLGizmoBase::Grabber::Grabber()
+ : center(Pointf(0.0, 0.0))
+ , angle_z(0.0f)
+{
+ color[0] = 1.0f;
+ color[1] = 1.0f;
+ color[2] = 1.0f;
+}
+
+void GLGizmoBase::Grabber::render(bool hover) const
+{
+ float min_x = -HalfSize;
+ float max_x = +HalfSize;
+ float min_y = -HalfSize;
+ float max_y = +HalfSize;
+
+ ::glColor3f((GLfloat)color[0], (GLfloat)color[1], (GLfloat)color[2]);
+
+ float angle_z_in_deg = angle_z * 180.0f / (float)PI;
+ ::glPushMatrix();
+ ::glTranslatef((GLfloat)center.x, (GLfloat)center.y, 0.0f);
+ ::glRotatef((GLfloat)angle_z_in_deg, 0.0f, 0.0f, 1.0f);
+
+ ::glDisable(GL_CULL_FACE);
+ ::glBegin(GL_TRIANGLES);
+ ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f);
+ ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f);
+ ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f);
+ ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f);
+ ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f);
+ ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f);
+ ::glEnd();
+ ::glEnable(GL_CULL_FACE);
+
+ if (hover)
+ {
+ min_x -= HoverOffset;
+ max_x += HoverOffset;
+ min_y -= HoverOffset;
+ max_y += HoverOffset;
+
+ ::glBegin(GL_LINE_LOOP);
+ ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f);
+ ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f);
+ ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f);
+ ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f);
+ ::glEnd();
+ }
+
+ ::glPopMatrix();
+}
+
+GLGizmoBase::GLGizmoBase()
+ : m_state(Off)
+ , m_hover_id(-1)
+{
+}
+
+GLGizmoBase::~GLGizmoBase()
+{
+}
+
+bool GLGizmoBase::init()
+{
+ return on_init();
+}
+
+GLGizmoBase::EState GLGizmoBase::get_state() const
+{
+ return m_state;
+}
+
+void GLGizmoBase::set_state(GLGizmoBase::EState state)
+{
+ m_state = state;
+}
+
+unsigned int GLGizmoBase::get_textures_id() const
+{
+ return m_textures[m_state].get_id();
+}
+
+int GLGizmoBase::get_textures_size() const
+{
+ return m_textures[Off].get_width();
+}
+
+int GLGizmoBase::get_hover_id() const
+{
+ return m_hover_id;
+}
+
+void GLGizmoBase::set_hover_id(int id)
+{
+ if (id < (int)m_grabbers.size())
+ m_hover_id = id;
+}
+
+void GLGizmoBase::start_dragging()
+{
+ on_start_dragging();
+}
+
+void GLGizmoBase::update(const Pointf& mouse_pos)
+{
+ if (m_hover_id != -1)
+ on_update(mouse_pos);
+}
+
+void GLGizmoBase::render(const BoundingBoxf3& box) const
+{
+ on_render(box);
+}
+
+void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const
+{
+ on_render_for_picking(box);
+}
+
+void GLGizmoBase::on_start_dragging()
+{
+}
+
+void GLGizmoBase::render_grabbers() const
+{
+ for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
+ {
+ m_grabbers[i].render(m_hover_id == i);
+ }
+}
+
+const float GLGizmoRotate::Offset = 5.0f;
+const unsigned int GLGizmoRotate::CircleResolution = 64;
+const unsigned int GLGizmoRotate::AngleResolution = 64;
+const unsigned int GLGizmoRotate::ScaleStepsCount = 60;
+const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount;
+const unsigned int GLGizmoRotate::ScaleLongEvery = 5;
+const float GLGizmoRotate::ScaleLongTooth = 2.0f;
+const float GLGizmoRotate::ScaleShortTooth = 1.0f;
+const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
+const float GLGizmoRotate::GrabberOffset = 5.0f;
+
+GLGizmoRotate::GLGizmoRotate()
+ : GLGizmoBase()
+ , m_angle_z(0.0f)
+ , m_center(Pointf(0.0, 0.0))
+ , m_radius(0.0f)
+{
+}
+
+bool GLGizmoRotate::on_init()
+{
+ std::string path = resources_dir() + "/icons/overlay/";
+
+ std::string filename = path + "rotate_off.png";
+ if (!m_textures[Off].load_from_file(filename, false))
+ return false;
+
+ filename = path + "rotate_hover.png";
+ if (!m_textures[Hover].load_from_file(filename, false))
+ return false;
+
+ filename = path + "rotate_on.png";
+ if (!m_textures[On].load_from_file(filename, false))
+ return false;
+
+ m_grabbers.push_back(Grabber());
+
+ return true;
+}
+
+void GLGizmoRotate::on_update(const Pointf& mouse_pos)
+{
+ Vectorf orig_dir(1.0, 0.0);
+ Vectorf new_dir = normalize(mouse_pos - m_center);
+ coordf_t theta = ::acos(clamp(-1.0, 1.0, dot(new_dir, orig_dir)));
+ if (cross(orig_dir, new_dir) < 0.0)
+ theta = 2.0 * (coordf_t)PI - theta;
+
+ if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0)
+ {
+ coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount;
+ theta = step * (coordf_t)std::round(theta / step);
+ }
+
+ if (theta == 2.0 * (coordf_t)PI)
+ theta = 0.0;
+
+ m_angle_z = (float)theta;
+}
+
+void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
+{
+ ::glDisable(GL_LIGHTING);
+ ::glDisable(GL_DEPTH_TEST);
+
+ const Pointf3& size = box.size();
+ m_center = box.center();
+ m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
+
+ ::glLineWidth(2.0f);
+ ::glColor3fv(BaseColor);
+
+ _render_circle();
+ _render_scale();
+ _render_snap_radii();
+ _render_reference_radius();
+
+ ::glColor3fv(HighlightColor);
+ _render_angle_z();
+ _render_grabber();
+}
+
+void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
+{
+ ::glDisable(GL_LIGHTING);
+ ::glDisable(GL_DEPTH_TEST);
+
+ m_grabbers[0].color[0] = 1.0f;
+ m_grabbers[0].color[1] = 1.0f;
+ m_grabbers[0].color[2] = 254.0f / 255.0f;
+ render_grabbers();
+}
+
+void GLGizmoRotate::_render_circle() const
+{
+ ::glBegin(GL_LINE_LOOP);
+ for (unsigned int i = 0; i < ScaleStepsCount; ++i)
+ {
+ float angle = (float)i * ScaleStepRad;
+ float x = m_center.x + ::cos(angle) * m_radius;
+ float y = m_center.y + ::sin(angle) * m_radius;
+ ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f);
+ }
+ ::glEnd();
+}
+
+void GLGizmoRotate::_render_scale() const
+{
+ float out_radius_long = m_radius + ScaleLongTooth;
+ float out_radius_short = m_radius + ScaleShortTooth;
+
+ ::glBegin(GL_LINES);
+ for (unsigned int i = 0; i < ScaleStepsCount; ++i)
+ {
+ float angle = (float)i * ScaleStepRad;
+ float cosa = ::cos(angle);
+ float sina = ::sin(angle);
+ float in_x = m_center.x + cosa * m_radius;
+ float in_y = m_center.y + sina * m_radius;
+ float out_x = (i % ScaleLongEvery == 0) ? m_center.x + cosa * out_radius_long : m_center.x + cosa * out_radius_short;
+ float out_y = (i % ScaleLongEvery == 0) ? m_center.y + sina * out_radius_long : m_center.y + sina * out_radius_short;
+ ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f);
+ ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f);
+ }
+ ::glEnd();
+}
+
+void GLGizmoRotate::_render_snap_radii() const
+{
+ float step = 2.0f * (float)PI / (float)SnapRegionsCount;
+
+ float in_radius = m_radius / 3.0f;
+ float out_radius = 2.0f * in_radius;
+
+ ::glBegin(GL_LINES);
+ for (unsigned int i = 0; i < SnapRegionsCount; ++i)
+ {
+ float angle = (float)i * step;
+ float cosa = ::cos(angle);
+ float sina = ::sin(angle);
+ float in_x = m_center.x + cosa * in_radius;
+ float in_y = m_center.y + sina * in_radius;
+ float out_x = m_center.x + cosa * out_radius;
+ float out_y = m_center.y + sina * out_radius;
+ ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f);
+ ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f);
+ }
+ ::glEnd();
+}
+
+void GLGizmoRotate::_render_reference_radius() const
+{
+ ::glBegin(GL_LINES);
+ ::glVertex3f((GLfloat)m_center.x, (GLfloat)m_center.y, 0.0f);
+ ::glVertex3f((GLfloat)m_center.x + m_radius + GrabberOffset, (GLfloat)m_center.y, 0.0f);
+ ::glEnd();
+}
+
+void GLGizmoRotate::_render_angle_z() const
+{
+ float step_angle = m_angle_z / AngleResolution;
+ float ex_radius = m_radius + GrabberOffset;
+
+ ::glBegin(GL_LINE_STRIP);
+ for (unsigned int i = 0; i <= AngleResolution; ++i)
+ {
+ float angle = (float)i * step_angle;
+ float x = m_center.x + ::cos(angle) * ex_radius;
+ float y = m_center.y + ::sin(angle) * ex_radius;
+ ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f);
+ }
+ ::glEnd();
+}
+
+void GLGizmoRotate::_render_grabber() const
+{
+ float grabber_radius = m_radius + GrabberOffset;
+ m_grabbers[0].center.x = m_center.x + ::cos(m_angle_z) * grabber_radius;
+ m_grabbers[0].center.y = m_center.y + ::sin(m_angle_z) * grabber_radius;
+ m_grabbers[0].angle_z = m_angle_z;
+
+ ::glColor3fv(BaseColor);
+ ::glBegin(GL_LINES);
+ ::glVertex3f((GLfloat)m_center.x, (GLfloat)m_center.y, 0.0f);
+ ::glVertex3f((GLfloat)m_grabbers[0].center.x, (GLfloat)m_grabbers[0].center.y, 0.0f);
+ ::glEnd();
+
+ ::memcpy((void*)m_grabbers[0].color, (const void*)HighlightColor, 3 * sizeof(float));
+ render_grabbers();
+}
+
+const float GLGizmoScale::Offset = 5.0f;
+
+GLGizmoScale::GLGizmoScale()
+ : GLGizmoBase()
+ , m_scale(1.0f)
+ , m_starting_scale(1.0f)
+{
+}
+
+float GLGizmoScale::get_scale() const
+{
+ return m_scale;
+}
+
+void GLGizmoScale::set_scale(float scale)
+{
+ m_starting_scale = scale;
+}
+
+bool GLGizmoScale::on_init()
+{
+ std::string path = resources_dir() + "/icons/overlay/";
+
+ std::string filename = path + "scale_off.png";
+ if (!m_textures[Off].load_from_file(filename, false))
+ return false;
+
+ filename = path + "scale_hover.png";
+ if (!m_textures[Hover].load_from_file(filename, false))
+ return false;
+
+ filename = path + "scale_on.png";
+ if (!m_textures[On].load_from_file(filename, false))
+ return false;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ {
+ m_grabbers.push_back(Grabber());
+ }
+
+ return true;
+}
+
+void GLGizmoScale::on_start_dragging()
+{
+ if (m_hover_id != -1)
+ m_starting_drag_position = m_grabbers[m_hover_id].center;
+}
+
+void GLGizmoScale::on_update(const Pointf& mouse_pos)
+{
+ Pointf center(0.5 * (m_grabbers[1].center.x + m_grabbers[0].center.x), 0.5 * (m_grabbers[3].center.y + m_grabbers[0].center.y));
+
+ coordf_t orig_len = length(m_starting_drag_position - center);
+ coordf_t new_len = length(mouse_pos - center);
+ coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0;
+
+ m_scale = m_starting_scale * (float)ratio;
+}
+
+void GLGizmoScale::on_render(const BoundingBoxf3& box) const
+{
+ ::glDisable(GL_LIGHTING);
+ ::glDisable(GL_DEPTH_TEST);
+
+ coordf_t min_x = box.min.x - (coordf_t)Offset;
+ coordf_t max_x = box.max.x + (coordf_t)Offset;
+ coordf_t min_y = box.min.y - (coordf_t)Offset;
+ coordf_t max_y = box.max.y + (coordf_t)Offset;
+
+ m_grabbers[0].center.x = min_x;
+ m_grabbers[0].center.y = min_y;
+ m_grabbers[1].center.x = max_x;
+ m_grabbers[1].center.y = min_y;
+ m_grabbers[2].center.x = max_x;
+ m_grabbers[2].center.y = max_y;
+ m_grabbers[3].center.x = min_x;
+ m_grabbers[3].center.y = max_y;
+
+ ::glLineWidth(2.0f);
+ ::glColor3fv(BaseColor);
+ // draw outline
+ ::glBegin(GL_LINE_LOOP);
+ for (unsigned int i = 0; i < 4; ++i)
+ {
+ ::glVertex3f((GLfloat)m_grabbers[i].center.x, (GLfloat)m_grabbers[i].center.y, 0.0f);
+ }
+ ::glEnd();
+
+ // draw grabbers
+ for (unsigned int i = 0; i < 4; ++i)
+ {
+ ::memcpy((void*)m_grabbers[i].color, (const void*)HighlightColor, 3 * sizeof(float));
+ }
+ render_grabbers();
+}
+
+void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const
+{
+ static const GLfloat INV_255 = 1.0f / 255.0f;
+
+ ::glDisable(GL_LIGHTING);
+ ::glDisable(GL_DEPTH_TEST);
+
+ for (unsigned int i = 0; i < 4; ++i)
+ {
+ m_grabbers[i].color[0] = 1.0f;
+ m_grabbers[i].color[1] = 1.0f;
+ m_grabbers[i].color[2] = (254.0f - (float)i) * INV_255;
+ }
+ render_grabbers();
+}
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
new file mode 100644
index 000000000..2baec8f9b
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLGizmo.hpp
@@ -0,0 +1,145 @@
+#ifndef slic3r_GLGizmo_hpp_
+#define slic3r_GLGizmo_hpp_
+
+#include "../../slic3r/GUI/GLTexture.hpp"
+#include "../../libslic3r/Point.hpp"
+
+#include <vector>
+
+namespace Slic3r {
+
+class BoundingBoxf3;
+class Pointf3;
+
+namespace GUI {
+
+class GLGizmoBase
+{
+protected:
+ static const float BaseColor[3];
+ static const float HighlightColor[3];
+
+ struct Grabber
+ {
+ static const float HalfSize;
+ static const float HoverOffset;
+
+ Pointf center;
+ float angle_z;
+ float color[3];
+
+ Grabber();
+ void render(bool hover) const;
+ };
+
+public:
+ enum EState
+ {
+ Off,
+ Hover,
+ On,
+ Num_States
+ };
+
+protected:
+ EState m_state;
+ // textures are assumed to be square and all with the same size in pixels, no internal check is done
+ GLTexture m_textures[Num_States];
+ int m_hover_id;
+ mutable std::vector<Grabber> m_grabbers;
+
+public:
+ GLGizmoBase();
+ virtual ~GLGizmoBase();
+
+ bool init();
+
+ EState get_state() const;
+ void set_state(EState state);
+
+ unsigned int get_textures_id() const;
+ int get_textures_size() const;
+
+ int get_hover_id() const;
+ void set_hover_id(int id);
+
+ void start_dragging();
+ void update(const Pointf& mouse_pos);
+
+ void render(const BoundingBoxf3& box) const;
+ void render_for_picking(const BoundingBoxf3& box) const;
+
+protected:
+ virtual bool on_init() = 0;
+ virtual void on_start_dragging();
+ virtual void on_update(const Pointf& mouse_pos) = 0;
+ virtual void on_render(const BoundingBoxf3& box) const = 0;
+ virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
+
+ void render_grabbers() const;
+};
+
+class GLGizmoRotate : public GLGizmoBase
+{
+ static const float Offset;
+ static const unsigned int CircleResolution;
+ static const unsigned int AngleResolution;
+ static const unsigned int ScaleStepsCount;
+ static const float ScaleStepRad;
+ static const unsigned int ScaleLongEvery;
+ static const float ScaleLongTooth;
+ static const float ScaleShortTooth;
+ static const unsigned int SnapRegionsCount;
+ static const float GrabberOffset;
+
+ float m_angle_z;
+
+ mutable Pointf m_center;
+ mutable float m_radius;
+
+public:
+ GLGizmoRotate();
+
+protected:
+ virtual bool on_init();
+ virtual void on_update(const Pointf& mouse_pos);
+ virtual void on_render(const BoundingBoxf3& box) const;
+ virtual void on_render_for_picking(const BoundingBoxf3& box) const;
+
+private:
+ void _render_circle() const;
+ void _render_scale() const;
+ void _render_snap_radii() const;
+ void _render_reference_radius() const;
+ void _render_angle_z() const;
+ void _render_grabber() const;
+};
+
+class GLGizmoScale : public GLGizmoBase
+{
+ static const float Offset;
+
+ float m_scale;
+
+ Pointf m_starting_drag_position;
+ float m_starting_scale;
+
+public:
+ GLGizmoScale();
+
+ float get_scale() const;
+ void set_scale(float scale);
+
+protected:
+ virtual bool on_init();
+ virtual void on_start_dragging();
+ virtual void on_update(const Pointf& mouse_pos);
+ virtual void on_render(const BoundingBoxf3& box) const;
+ virtual void on_render_for_picking(const BoundingBoxf3& box) const;
+};
+
+} // namespace GUI
+} // namespace Slic3r
+
+#endif // slic3r_GLGizmo_hpp_
+
diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp
index ce9a80f05..903f6c347 100644
--- a/xs/src/slic3r/GUI/GLShader.cpp
+++ b/xs/src/slic3r/GUI/GLShader.cpp
@@ -2,6 +2,9 @@
#include "GLShader.hpp"
+#include "../../libslic3r/Utils.hpp"
+#include <boost/nowide/fstream.hpp>
+
#include <string>
#include <utility>
#include <assert.h>
@@ -22,7 +25,7 @@ inline std::string gl_get_string_safe(GLenum param)
return std::string(value ? value : "N/A");
}
-bool GLShader::load(const char *fragment_shader, const char *vertex_shader)
+bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_shader)
{
std::string gl_version = gl_get_string_safe(GL_VERSION);
int major = atoi(gl_version.c_str());
@@ -123,6 +126,41 @@ bool GLShader::load(const char *fragment_shader, const char *vertex_shader)
return true;
}
+bool GLShader::load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename)
+{
+ const std::string& path = resources_dir() + "/shaders/";
+
+ boost::nowide::ifstream vs(path + std::string(vertex_shader_filename), boost::nowide::ifstream::binary);
+ if (!vs.good())
+ return false;
+
+ vs.seekg(0, vs.end);
+ int file_length = vs.tellg();
+ vs.seekg(0, vs.beg);
+ std::string vertex_shader(file_length, '\0');
+ vs.read(const_cast<char*>(vertex_shader.data()), file_length);
+ if (!vs.good())
+ return false;
+
+ vs.close();
+
+ boost::nowide::ifstream fs(path + std::string(fragment_shader_filename), boost::nowide::ifstream::binary);
+ if (!fs.good())
+ return false;
+
+ fs.seekg(0, fs.end);
+ file_length = fs.tellg();
+ fs.seekg(0, fs.beg);
+ std::string fragment_shader(file_length, '\0');
+ fs.read(const_cast<char*>(fragment_shader.data()), file_length);
+ if (!fs.good())
+ return false;
+
+ fs.close();
+
+ return load_from_text(fragment_shader.c_str(), vertex_shader.c_str());
+}
+
void GLShader::release()
{
if (this->shader_program_id) {
diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp
index d91463f19..032640d8d 100644
--- a/xs/src/slic3r/GUI/GLShader.hpp
+++ b/xs/src/slic3r/GUI/GLShader.hpp
@@ -16,7 +16,9 @@ public:
{}
~GLShader();
- bool load(const char *fragment_shader, const char *vertex_shader);
+ bool load_from_text(const char *fragment_shader, const char *vertex_shader);
+ bool load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename);
+
void release();
int get_attrib_location(const char *name) const;
diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp
new file mode 100644
index 000000000..593362e54
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLTexture.cpp
@@ -0,0 +1,185 @@
+#include "GLTexture.hpp"
+
+#include <GL/glew.h>
+
+#include <wx/image.h>
+
+#include <vector>
+#include <algorithm>
+
+namespace Slic3r {
+namespace GUI {
+
+GLTexture::GLTexture()
+ : m_id(0)
+ , m_width(0)
+ , m_height(0)
+ , m_source("")
+{
+}
+
+GLTexture::~GLTexture()
+{
+ reset();
+}
+
+bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmaps)
+{
+ reset();
+
+ // Load a PNG with an alpha channel.
+ wxImage image;
+ if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG))
+ {
+ reset();
+ return false;
+ }
+
+ m_width = image.GetWidth();
+ m_height = image.GetHeight();
+ int n_pixels = m_width * m_height;
+
+ if (n_pixels <= 0)
+ {
+ reset();
+ return false;
+ }
+
+ // Get RGB & alpha raw data from wxImage, pack them into an array.
+ unsigned char* img_rgb = image.GetData();
+ if (img_rgb == nullptr)
+ {
+ reset();
+ return false;
+ }
+
+ unsigned char* img_alpha = image.GetAlpha();
+
+ std::vector<unsigned char> data(n_pixels * 4, 0);
+ for (int i = 0; i < n_pixels; ++i)
+ {
+ int data_id = i * 4;
+ int img_id = i * 3;
+ data[data_id + 0] = img_rgb[img_id + 0];
+ data[data_id + 1] = img_rgb[img_id + 1];
+ data[data_id + 2] = img_rgb[img_id + 2];
+ data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
+ }
+
+ // sends data to gpu
+ ::glGenTextures(1, &m_id);
+ ::glBindTexture(GL_TEXTURE_2D, m_id);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ if (generate_mipmaps)
+ {
+ // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
+ _generate_mipmaps(image);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ }
+ else
+ {
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ }
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ m_source = filename;
+ return true;
+}
+
+void GLTexture::reset()
+{
+ if (m_id != 0)
+ ::glDeleteTextures(1, &m_id);
+
+ m_id = 0;
+ m_width = 0;
+ m_height = 0;
+ m_source = "";
+}
+
+unsigned int GLTexture::get_id() const
+{
+ return m_id;
+}
+
+int GLTexture::get_width() const
+{
+ return m_width;
+}
+
+int GLTexture::get_height() const
+{
+ return m_height;
+}
+
+const std::string& GLTexture::get_source() const
+{
+ return m_source;
+}
+
+void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
+{
+ ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ ::glDisable(GL_LIGHTING);
+ ::glEnable(GL_BLEND);
+ ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ ::glEnable(GL_TEXTURE_2D);
+
+ ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id);
+
+ ::glBegin(GL_QUADS);
+ ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f);
+ ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f);
+ ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f);
+ ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f);
+ ::glEnd();
+
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ ::glDisable(GL_TEXTURE_2D);
+ ::glDisable(GL_BLEND);
+ ::glEnable(GL_LIGHTING);
+}
+
+void GLTexture::_generate_mipmaps(wxImage& image)
+{
+ int w = image.GetWidth();
+ int h = image.GetHeight();
+ GLint level = 0;
+ std::vector<unsigned char> data(w * h * 4, 0);
+
+ while ((w > 1) && (h > 1))
+ {
+ ++level;
+
+ w = std::max(w / 2, 1);
+ h = std::max(h / 2, 1);
+
+ int n_pixels = w * h;
+
+ image = image.ResampleBicubic(w, h);
+
+ unsigned char* img_rgb = image.GetData();
+ unsigned char* img_alpha = image.GetAlpha();
+
+ data.resize(n_pixels * 4);
+ for (int i = 0; i < n_pixels; ++i)
+ {
+ int data_id = i * 4;
+ int img_id = i * 3;
+ data[data_id + 0] = img_rgb[img_id + 0];
+ data[data_id + 1] = img_rgb[img_id + 1];
+ data[data_id + 2] = img_rgb[img_id + 2];
+ data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
+ }
+
+ ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ }
+}
+
+} // namespace GUI
+} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/xs/src/slic3r/GUI/GLTexture.hpp
new file mode 100644
index 000000000..70480c605
--- /dev/null
+++ b/xs/src/slic3r/GUI/GLTexture.hpp
@@ -0,0 +1,41 @@
+#ifndef slic3r_GLTexture_hpp_
+#define slic3r_GLTexture_hpp_
+
+#include <string>
+
+class wxImage;
+
+namespace Slic3r {
+namespace GUI {
+
+ class GLTexture
+ {
+ private:
+ unsigned int m_id;
+ int m_width;
+ int m_height;
+ std::string m_source;
+
+ public:
+ GLTexture();
+ ~GLTexture();
+
+ bool load_from_file(const std::string& filename, bool generate_mipmaps);
+ void reset();
+
+ unsigned int get_id() const;
+ int get_width() const;
+ int get_height() const;
+ const std::string& get_source() const;
+
+ static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
+
+ private:
+ void _generate_mipmaps(wxImage& image);
+ };
+
+} // namespace GUI
+} // namespace Slic3r
+
+#endif // slic3r_GLTexture_hpp_
+
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 6a907291e..29f35293b 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -8,7 +8,8 @@
GLShader();
~GLShader();
- bool load(const char *fragment_shader, const char *vertex_shader);
+ bool load_from_text(const char *fragment_shader, const char *vertex_shader);
+ bool load_from_file(const char *fragment_shader, const char *vertex_shader);
void release();
int get_attrib_location(const char *name) const;
@@ -92,9 +93,6 @@
int count()
%code{% RETVAL = THIS->volumes.size(); %};
- std::vector<double> get_current_print_zs(bool active_only)
- %code{% RETVAL = THIS->get_current_print_zs(active_only); %};
-
void set_range(double low, double high);
void render_VBOs() const;
@@ -155,11 +153,457 @@ GLVolumeCollection::arrayref()
%package{Slic3r::GUI::_3DScene};
%{
+std::string
+get_gl_info(format_as_html, extensions)
+ bool format_as_html;
+ bool extensions;
+ CODE:
+ RETVAL = _3DScene::get_gl_info(format_as_html, extensions);
+ OUTPUT:
+ RETVAL
+
+bool
+use_VBOs()
+ CODE:
+ RETVAL = _3DScene::use_VBOs();
+ OUTPUT:
+ RETVAL
+
+bool
+add_canvas(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::add_canvas((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+bool
+remove_canvas(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::remove_canvas((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
void
-_glew_init()
+remove_all_canvases()
CODE:
- _3DScene::_glew_init();
+ _3DScene::remove_all_canvases();
+
+void
+set_active(canvas, active)
+ SV *canvas;
+ bool active;
+ CODE:
+ _3DScene::set_active((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), active);
+
+unsigned int
+get_volumes_count(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::get_volumes_count((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+void
+reset_volumes(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::reset_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
+deselect_volumes(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::deselect_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
+select_volume(canvas, id)
+ SV *canvas;
+ unsigned int id;
+ CODE:
+ _3DScene::select_volume((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id);
+
+void
+update_volumes_selection(canvas, selections)
+ SV *canvas;
+ std::vector<int> selections;
+ CODE:
+ _3DScene::update_volumes_selection((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections);
+
+bool
+check_volumes_outside_state(canvas, config)
+ SV *canvas;
+ DynamicPrintConfig *config;
+ CODE:
+ RETVAL = _3DScene::check_volumes_outside_state((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), config);
+ OUTPUT:
+ RETVAL
+
+bool
+move_volume_up(canvas, id)
+ SV *canvas;
+ unsigned int id;
+ CODE:
+ RETVAL = _3DScene::move_volume_up((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id);
+ OUTPUT:
+ RETVAL
+
+bool
+move_volume_down(canvas, id)
+ SV *canvas;
+ unsigned int id;
+ CODE:
+ RETVAL = _3DScene::move_volume_down((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id);
+ OUTPUT:
+ RETVAL
+
+void
+set_objects_selections(canvas, selections)
+ SV *canvas;
+ std::vector<int> selections;
+ CODE:
+ _3DScene::set_objects_selections((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections);
+
+void
+set_config(canvas, config)
+ SV *canvas;
+ DynamicPrintConfig *config;
+ CODE:
+ _3DScene::set_config((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), config);
+
+void
+set_print(canvas, print)
+ SV *canvas;
+ Print *print;
+ CODE:
+ _3DScene::set_print((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print);
+
+void
+set_model(canvas, model)
+ SV *canvas;
+ Model *model;
+ CODE:
+ _3DScene::set_model((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model);
+
+void
+set_bed_shape(canvas, shape)
+ SV *canvas;
+ Pointfs shape;
+ CODE:
+ _3DScene::set_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), shape);
+
+void
+set_auto_bed_shape(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::set_auto_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+Clone<BoundingBoxf3>
+get_volumes_bounding_box(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::get_volumes_bounding_box((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+void
+set_axes_length(canvas, length)
+ SV *canvas;
+ float length;
+ CODE:
+ _3DScene::set_axes_length((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), length);
+
+void
+set_cutting_plane(canvas, z, polygons)
+ SV *canvas;
+ float z;
+ ExPolygons polygons;
+ CODE:
+ _3DScene::set_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), z, polygons);
+
+void
+set_color_by(canvas, value)
+ SV *canvas;
+ std::string value;
+ CODE:
+ _3DScene::set_color_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value);
+
+void
+set_select_by(canvas, value)
+ SV *canvas;
+ std::string value;
+ CODE:
+ _3DScene::set_select_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value);
+
+void
+set_drag_by(canvas, value)
+ SV *canvas;
+ std::string value;
+ CODE:
+ _3DScene::set_drag_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value);
+
+bool
+is_layers_editing_enabled(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::is_layers_editing_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+bool
+is_layers_editing_allowed(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::is_layers_editing_allowed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+bool
+is_shader_enabled(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::is_shader_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+bool
+is_reload_delayed(canvas)
+ SV *canvas;
+ CODE:
+ RETVAL = _3DScene::is_reload_delayed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+ OUTPUT:
+ RETVAL
+
+void
+enable_layers_editing(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_layers_editing((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_warning_texture(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_warning_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_legend_texture(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_picking(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_picking((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_moving(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_moving((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_gizmos(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_gizmos((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_shader(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+enable_force_zoom_to_bed(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_force_zoom_to_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
+allow_multisample(canvas, allow)
+ SV *canvas;
+ bool allow;
+ CODE:
+ _3DScene::allow_multisample((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), allow);
+
+void
+zoom_to_bed(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::zoom_to_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
+zoom_to_volumes(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::zoom_to_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
+select_view(canvas, direction)
+ SV *canvas;
+ std::string direction;
+ CODE:
+ _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction);
+
+void
+set_viewport_from_scene(canvas, other)
+ SV *canvas;
+ SV *other;
+ CODE:
+ _3DScene::set_viewport_from_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (wxGLCanvas*)wxPli_sv_2_object(aTHX_ other, "Wx::GLCanvas"));
+
+void
+update_volumes_colors_by_extruder(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::update_volumes_colors_by_extruder((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
+render(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::render((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+std::vector<double>
+get_current_print_zs(canvas, active_only)
+ SV *canvas;
+ bool active_only;
+ CODE:
+ RETVAL = _3DScene::get_current_print_zs((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), active_only);
+ OUTPUT:
+ RETVAL
+
+void
+set_toolpaths_range(canvas, low, high)
+ SV *canvas;
+ double low;
+ double high;
+ CODE:
+ _3DScene::set_toolpaths_range((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), low, high);
+
+void
+register_on_viewport_changed_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_viewport_changed_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_double_click_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_double_click_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_right_click_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_right_click_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_select_object_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_select_object_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_model_update_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_model_update_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_remove_object_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_remove_object_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_arrange_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_arrange_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_rotate_object_left_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_rotate_object_left_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_rotate_object_right_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_rotate_object_right_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_scale_object_uniformly_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_scale_object_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_increase_objects_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_increase_objects_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_decrease_objects_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_decrease_objects_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_instance_moved_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_instance_moved_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_wipe_tower_moved_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_wipe_tower_moved_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_enable_action_buttons_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_enable_action_buttons_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_gizmo_scale_uniformly_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_gizmo_scale_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
unsigned int
finalize_legend_texture()
@@ -218,41 +662,61 @@ reset_warning_texture()
CODE:
_3DScene::reset_warning_texture();
+std::vector<int>
+load_model_object(canvas, model_object, obj_idx, instance_idxs)
+ SV *canvas;
+ ModelObject *model_object;
+ int obj_idx;
+ std::vector<int> instance_idxs;
+ CODE:
+ RETVAL = _3DScene::load_object((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model_object, obj_idx, instance_idxs);
+ OUTPUT:
+ RETVAL
+
+std::vector<int>
+load_model(canvas, model, obj_idx)
+ SV *canvas;
+ Model *model;
+ int obj_idx;
+ CODE:
+ RETVAL = _3DScene::load_object((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model, obj_idx);
+ OUTPUT:
+ RETVAL
+
void
-_load_print_toolpaths(print, volumes, tool_colors, use_VBOs)
- Print *print;
- GLVolumeCollection *volumes;
- std::vector<std::string> tool_colors;
- int use_VBOs;
+reload_scene(canvas, force)
+ SV *canvas;
+ bool force;
+ CODE:
+ _3DScene::reload_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), force);
+
+void
+load_print_toolpaths(canvas)
+ SV *canvas;
CODE:
- _3DScene::_load_print_toolpaths(print, volumes, tool_colors, use_VBOs != 0);
+ _3DScene::load_print_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
void
-_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs)
- PrintObject *print_object;
- GLVolumeCollection *volumes;
+load_print_object_toolpaths(canvas, print_object, tool_colors)
+ SV *canvas;
+ PrintObject *print_object;
std::vector<std::string> tool_colors;
- int use_VBOs;
CODE:
- _3DScene::_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs != 0);
+ _3DScene::load_print_object_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object, tool_colors);
void
-_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs)
- Print *print;
- GLVolumeCollection *volumes;
+load_wipe_tower_toolpaths(canvas, tool_colors)
+ SV *canvas;
std::vector<std::string> tool_colors;
- int use_VBOs;
CODE:
- _3DScene::_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs != 0);
+ _3DScene::load_wipe_tower_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tool_colors);
void
-load_gcode_preview(print, preview_data, volumes, str_tool_colors, use_VBOs)
- Print *print;
+load_gcode_preview(canvas, preview_data, str_tool_colors)
+ SV *canvas;
GCodePreviewData *preview_data;
- GLVolumeCollection *volumes;
std::vector<std::string> str_tool_colors;
- int use_VBOs;
CODE:
- _3DScene::load_gcode_preview(print, preview_data, volumes, str_tool_colors, use_VBOs != 0);
+ _3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors);
%}
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index ef9c5345f..e05112932 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -52,6 +52,9 @@ _constant()
int region_count()
%code%{ RETVAL = THIS->print()->regions.size(); %};
+ int region_volumes_count()
+ %code%{ RETVAL = THIS->region_volumes.size(); %};
+
Ref<Print> print();
Ref<ModelObject> model_object();
Ref<StaticPrintConfig> config()
@@ -119,15 +122,6 @@ _constant()
RETVAL.push_back(slicing_params.layer_height);
%};
- void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
- %code%{
- THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile);
- adjust_layer_height_profile(
- THIS->slicing_parameters(), THIS->model_object()->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
- THIS->model_object()->layer_height_profile_valid = true;
- THIS->layer_height_profile_valid = false;
- %};
-
void reset_layer_height_profile();
int ptr()