diff --git a/resources/icons/legend_cog.svg b/resources/icons/legend_cog.svg
new file mode 100644
index 000000000..9a55fb7f5
--- /dev/null
+++ b/resources/icons/legend_cog.svg
@@ -0,0 +1,50 @@
+
+
+
+
diff --git a/resources/shaders/toolpaths_cog.fs b/resources/shaders/toolpaths_cog.fs
new file mode 100644
index 000000000..f88d79b96
--- /dev/null
+++ b/resources/shaders/toolpaths_cog.fs
@@ -0,0 +1,18 @@
+#version 110
+
+const vec4 BLACK = vec4(vec3(0.1), 1.0);
+const vec4 WHITE = vec4(vec3(1.0), 1.0);
+
+const float emission_factor = 0.25;
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+varying vec3 world_position;
+uniform vec3 world_center;
+
+void main()
+{
+ vec3 delta = world_position - world_center;
+ vec4 color = delta.x * delta.y * delta.z > 0.0 ? BLACK : WHITE;
+ gl_FragColor = vec4(vec3(intensity.y) + color.rgb * (intensity.x + emission_factor), 1.0);
+}
diff --git a/resources/shaders/toolpaths_cog.vs b/resources/shaders/toolpaths_cog.vs
new file mode 100644
index 000000000..c7b1abfdb
--- /dev/null
+++ b/resources/shaders/toolpaths_cog.vs
@@ -0,0 +1,40 @@
+#version 110
+
+#define INTENSITY_CORRECTION 0.6
+
+// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
+const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
+#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SHININESS 20.0
+
+// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
+const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
+#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
+
+#define INTENSITY_AMBIENT 0.3
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+varying vec3 world_position;
+
+void main()
+{
+ // First transform the normal into camera space and normalize the result.
+ vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
+
+ // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+ // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+ float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
+
+ intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+ vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
+
+ // Perform the same lighting calculation for the 2nd light source (no specular applied).
+ NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
+ intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
+
+ world_position = gl_Vertex.xyz;
+ gl_Position = ftransform();
+}
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index db0e54e60..f2c3ef083 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -165,8 +165,9 @@ namespace ImGui
const wchar_t LegendColorChanges = 0x2612;
const wchar_t LegendPausePrints = 0x2613;
const wchar_t LegendCustomGCodes = 0x2614;
- const wchar_t LegendShells = 0x2615;
- const wchar_t LegendToolMarker = 0x2616;
+ const wchar_t LegendCOG = 0x2615;
+ const wchar_t LegendShells = 0x2616;
+ const wchar_t LegendToolMarker = 0x2617;
// void MyFunction(const char* name, const MyMatrix44& v);
}
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index da55ae4ff..80a4284a7 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -72,6 +72,8 @@
#define ENABLE_SHOW_NON_MANIFOLD_EDGES (1 && ENABLE_2_5_0_ALPHA1)
// Enable rework of Reload from disk command
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
+// Enable showing toolpaths center of gravity
+#define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1)
#endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 367846f0c..a4808a0cf 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -160,6 +160,66 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move
move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
}
+#if ENABLE_SHOW_TOOLPATHS_COG
+void GCodeViewer::COG::render()
+{
+ if (!m_visible)
+ return;
+
+ init();
+
+ GLShaderProgram* shader = wxGetApp().get_shader("toolpaths_cog");
+ if (shader == nullptr)
+ return;
+
+ shader->start_using();
+
+ glsafe(::glDisable(GL_DEPTH_TEST));
+
+ glsafe(::glPushMatrix());
+ const Vec3d position = cog();
+ glsafe(::glTranslated(position.x(), position.y(), position.z()));
+ if (m_fixed_size) {
+ const double inv_zoom = wxGetApp().plater()->get_camera().get_inv_zoom();
+ glsafe(::glScaled(inv_zoom, inv_zoom, inv_zoom));
+ }
+ m_model.render();
+
+ glsafe(::glPopMatrix());
+
+ shader->stop_using();
+
+ ////Show ImGui window
+ //static float last_window_width = 0.0f;
+ //static size_t last_text_length = 0;
+
+ //ImGuiWrapper& imgui = *wxGetApp().imgui();
+ //const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
+ //imgui.set_next_window_pos(0.5f * static_cast(cnv_size.get_width()), 0.0f, ImGuiCond_Always, 0.5f, 0.0f);
+ //ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
+ //ImGui::SetNextWindowBgAlpha(0.25f);
+ //imgui.begin(std::string("COG"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
+ //imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Center of mass") + ":");
+ //ImGui::SameLine();
+ //char buf[1024];
+ //const Vec3d position = cog();
+ //sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z());
+ //imgui.text(std::string(buf));
+
+ //// force extra frame to automatically update window size
+ //const float width = ImGui::GetWindowWidth();
+ //const size_t length = strlen(buf);
+ //if (width != last_window_width || length != last_text_length) {
+ // last_window_width = width;
+ // last_text_length = length;
+ // imgui.set_requires_extra_frame();
+ //}
+
+ //imgui.end();
+ //ImGui::PopStyleVar();
+}
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+
#if ENABLE_PREVIEW_LAYER_TIME
float GCodeViewer::Extrusions::Range::step_size(EType type) const
{
@@ -955,6 +1015,9 @@ unsigned int GCodeViewer::get_options_visibility_flags() const
flags = set_flag(flags, static_cast(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(EMoveType::Color_change));
flags = set_flag(flags, static_cast(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(EMoveType::Pause_Print));
flags = set_flag(flags, static_cast(Preview::OptionType::CustomGCodes), is_toolpath_move_type_visible(EMoveType::Custom_GCode));
+#if ENABLE_SHOW_TOOLPATHS_COG
+ flags = set_flag(flags, static_cast(Preview::OptionType::CenterOfGravity), m_cog.is_visible());
+#endif // ENABLE_SHOW_TOOLPATHS_COG
flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible);
flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible());
#if !ENABLE_PREVIEW_LAYOUT
@@ -978,6 +1041,9 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags)
set_toolpath_move_type_visible(EMoveType::Color_change, is_flag_set(static_cast(Preview::OptionType::ColorChanges)));
set_toolpath_move_type_visible(EMoveType::Pause_Print, is_flag_set(static_cast(Preview::OptionType::PausePrints)));
set_toolpath_move_type_visible(EMoveType::Custom_GCode, is_flag_set(static_cast(Preview::OptionType::CustomGCodes)));
+#if ENABLE_SHOW_TOOLPATHS_COG
+ m_cog.set_visible(is_flag_set(static_cast(Preview::OptionType::CenterOfGravity)));
+#endif // ENABLE_SHOW_TOOLPATHS_COG
m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells));
m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker)));
#if !ENABLE_PREVIEW_LAYOUT
@@ -1537,6 +1603,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
if (wxGetApp().is_editor())
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box);
+#if ENABLE_SHOW_TOOLPATHS_COG
+ m_cog.reset();
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+
m_sequential_view.gcode_ids.clear();
for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
const GCodeProcessorResult::MoveVertex& move = gcode_result.moves[i];
@@ -1551,18 +1621,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
std::vector instances_offsets(m_buffers.size());
std::vector options_zs;
- size_t seams_count = 0;
std::vector biased_seams_ids;
// toolpaths data -> extract vertices from result
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
- if (curr.type == EMoveType::Seam) {
- ++seams_count;
+ if (curr.type == EMoveType::Seam)
biased_seams_ids.push_back(i - biased_seams_ids.size() - 1);
- }
- size_t move_id = i - seams_count;
+ const size_t move_id = i - biased_seams_ids.size();
// skip first vertex
if (i == 0)
@@ -1570,6 +1637,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1];
+#if ENABLE_SHOW_TOOLPATHS_COG
+ if (curr.type == EMoveType::Extrude &&
+ curr.extrusion_role != erSkirt &&
+ curr.extrusion_role != erSupportMaterial &&
+ curr.extrusion_role != erSupportMaterialInterface &&
+ curr.extrusion_role != erWipeTower &&
+ curr.extrusion_role != erCustom &&
+ curr.extrusion_role != erMixed) {
+ const Vec3d curr_pos = curr.position.cast();
+ const Vec3d prev_pos = prev.position.cast();
+ m_cog.add_segment(curr_pos, prev_pos, curr.mm3_per_mm * (curr_pos - prev_pos).norm());
+ }
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+
// update progress dialog
++progress_count;
if (progress_dialog != nullptr && progress_count % progress_threshold == 0) {
@@ -1893,14 +1974,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
using VboIndexList = std::vector;
std::vector vbo_indices(m_buffers.size());
- seams_count = 0;
+ size_t seams_count = 0;
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessorResult::MoveVertex& curr = gcode_result.moves[i];
if (curr.type == EMoveType::Seam)
++seams_count;
- size_t move_id = i - seams_count;
+ const size_t move_id = i - seams_count;
// skip first vertex
if (i == 0)
@@ -4074,15 +4155,6 @@ void GCodeViewer::render_legend(float& legend_height)
};
#if ENABLE_LEGEND_TOOLBAR_ICONS
-// auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) {
-// const float margin = 3.0f;
-// const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size));
-// window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16);
-// };
-// auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) {
-// const float margin = 3.0f;
-// window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f);
-// };
auto image_icon = [&imgui](ImGuiWindow& window, const ImVec2& pos, float size, const wchar_t& icon_id) {
ImGuiIO& io = ImGui::GetIO();
const ImTextureID tex_id = io.Fonts->TexID;
@@ -4091,17 +4163,17 @@ void GCodeViewer::render_legend(float& legend_height)
const ImFontAtlas::CustomRect* const rect = imgui.GetTextureCustomRect(icon_id);
const ImVec2 uv0 = { static_cast(rect->X) / tex_w, static_cast(rect->Y) / tex_h };
const ImVec2 uv1 = { static_cast(rect->X + rect->Width) / tex_w, static_cast(rect->Y + rect->Height) / tex_h };
- window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 1.0f }));
+ window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f }));
};
#else
- auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) {
- const float margin = 3.0f;
+ auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const ColorRGBA& color) {
+ const float margin = 3.0f;
const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size));
- window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16);
+ window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGuiWrapper::to_ImU32(color), 16);
};
- auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) {
+ auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const ColorRGBA& color) {
const float margin = 3.0f;
- window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f);
+ window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGuiWrapper::to_ImU32(color), 3.0f);
};
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
@@ -4190,12 +4262,41 @@ void GCodeViewer::render_legend(float& legend_height)
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
});
ImGui::SameLine();
+#if ENABLE_SHOW_TOOLPATHS_COG
+#if ENABLE_LEGEND_TOOLBAR_ICONS
+ toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
+ image_icon(window, pos, size, ImGui::LegendCOG);
+ });
+#else
+ toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [](ImGuiWindow& window, const ImVec2& pos, float size) {
+ const ImU32 black = ImGuiWrapper::to_ImU32({ 0.0f, 0.0f, 0.0f, 1.0f });
+ const ImU32 white = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f });
+ const float margin = 3.0f;
+ const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size));
+ const float radius = 0.5f * (size - 2.0f * margin);
+ window.DrawList->PathArcToFast(center, radius, 0, 3);
+ window.DrawList->PathLineTo(center);
+ window.DrawList->PathFillConvex(black);
+ window.DrawList->PathArcToFast(center, radius, 3, 6);
+ window.DrawList->PathLineTo(center);
+ window.DrawList->PathFillConvex(white);
+ window.DrawList->PathArcToFast(center, radius, 6, 9);
+ window.DrawList->PathLineTo(center);
+ window.DrawList->PathFillConvex(black);
+ window.DrawList->PathArcToFast(center, radius, 9, 12);
+ window.DrawList->PathLineTo(center);
+ window.DrawList->PathFillConvex(white);
+ window.DrawList->AddCircle(center, radius, black, 16);
+ });
+#endif // ENABLE_LEGEND_TOOLBAR_ICONS
+ ImGui::SameLine();
+#endif // ENABLE_SHOW_TOOLPATHS_COG
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendShells);
#else
toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [](ImGuiWindow& window, const ImVec2& pos, float size) {
- const ImU32 color = ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 1.0f });
+ const ImU32 color = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f });
const float margin = 3.0f;
const float proj = 0.25f * size;
window.DrawList->AddRect({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin - proj, pos.y + margin + proj }, color);
@@ -4212,11 +4313,11 @@ void GCodeViewer::render_legend(float& legend_height)
image_icon(window, pos, size, ImGui::LegendToolMarker);
#else
toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [](ImGuiWindow& window, const ImVec2& pos, float size) {
- const ImU32 color = ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 0.8f });
+ const ImU32 color = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 0.8f });
const float margin = 3.0f;
const ImVec2 p1(0.5f * (pos.x + pos.x + size), pos.y + size - margin);
- const ImVec2 p2 = ImVec2(p1.x + 0.25f * size, p1.y - 0.25f * size);
- const ImVec2 p3 = ImVec2(p1.x - 0.25f * size, p1.y - 0.25f * size);
+ const ImVec2 p2(p1.x + 0.25f * size, p1.y - 0.25f * size);
+ const ImVec2 p3(p1.x - 0.25f * size, p1.y - 0.25f * size);
window.DrawList->AddTriangleFilled(p1, p2, p3, color);
const float mid_x = 0.5f * (pos.x + pos.x + size);
window.DrawList->AddRectFilled({ mid_x - 0.09375f * size, p1.y - 0.25f * size }, { mid_x + 0.09375f * size, pos.y + margin }, color);
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index ecebb2641..22e866fe6 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -380,6 +380,52 @@ class GCodeViewer
bool visible{ false };
};
+#if ENABLE_SHOW_TOOLPATHS_COG
+ // helper to render center of gravity
+ class COG
+ {
+ GLModel m_model;
+ bool m_visible{ false };
+ // whether or not to render the model with fixed screen size
+ bool m_fixed_size{ true };
+ double m_total_mass{ 0.0 };
+ Vec3d m_position{ Vec3d::Zero() };
+
+ public:
+ void render();
+
+ void reset() {
+ m_position = Vec3d::Zero();
+ m_total_mass = 0.0;
+ }
+
+ bool is_visible() const { return m_visible; }
+ void set_visible(bool visible) { m_visible = visible; }
+
+ void add_segment(const Vec3d& v1, const Vec3d& v2, double mass) {
+ assert(mass > 0.0);
+ m_position += mass * 0.5 * (v1 + v2);
+ m_total_mass += mass;
+ }
+
+ Vec3d cog() const { return (m_total_mass > 0.0) ? (Vec3d)(m_position / m_total_mass) : Vec3d::Zero(); }
+
+ private:
+ void init() {
+ if (m_model.is_initialized())
+ return;
+
+ const float radius = m_fixed_size ? 10.0f : 1.0f;
+
+#if ENABLE_GLBEGIN_GLEND_REMOVAL
+ m_model.init_from(smooth_sphere(32, radius));
+#else
+ m_model.init_from(its_make_sphere(radius, PI / 32.0));
+#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
+ }
+ };
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+
// helper to render extrusion paths
struct Extrusions
{
@@ -734,6 +780,9 @@ private:
Extrusions m_extrusions;
SequentialView m_sequential_view;
Shells m_shells;
+#if ENABLE_SHOW_TOOLPATHS_COG
+ COG m_cog;
+#endif // ENABLE_SHOW_TOOLPATHS_COG
EViewType m_view_type{ EViewType::FeatureType };
bool m_legend_enabled{ true };
#if ENABLE_PREVIEW_LAYOUT
@@ -779,6 +828,9 @@ public:
void reset();
void render();
+#if ENABLE_SHOW_TOOLPATHS_COG
+ void render_cog() { m_cog.render(); }
+#endif // ENABLE_SHOW_TOOLPATHS_COG
bool has_data() const { return !m_roles.empty(); }
bool can_export_toolpaths() const;
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 84cc2e555..e16f7cfb1 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1600,6 +1600,10 @@ void GLCanvas3D::render()
#if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center();
#endif // ENABLE_RENDER_SELECTION_CENTER
+#if ENABLE_SHOW_TOOLPATHS_COG
+ if (!m_main_toolbar.is_enabled())
+ _render_gcode_cog();
+#endif // ENABLE_SHOW_TOOLPATHS_COG
// we need to set the mouse's scene position here because the depth buffer
// could be invalidated by the following gizmo render methods
@@ -5415,6 +5419,13 @@ void GLCanvas3D::_render_gcode()
m_gcode_viewer.render();
}
+#if ENABLE_SHOW_TOOLPATHS_COG
+void GLCanvas3D::_render_gcode_cog()
+{
+ m_gcode_viewer.render_cog();
+}
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+
void GLCanvas3D::_render_selection()
{
float scale_factor = 1.0;
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 93cd8f424..33e17d5dd 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -938,6 +938,9 @@ private:
void _render_bed_for_picking(bool bottom);
void _render_objects(GLVolumeCollection::ERenderType type);
void _render_gcode();
+#if ENABLE_SHOW_TOOLPATHS_COG
+ void _render_gcode_cog();
+#endif // ENABLE_SHOW_TOOLPATHS_COG
void _render_selection();
void _render_sequential_clearance();
#if ENABLE_RENDER_SELECTION_CENTER
diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp
index 8ac1e5534..2cf599001 100644
--- a/src/slic3r/GUI/GLModel.cpp
+++ b/src/slic3r/GUI/GLModel.cpp
@@ -1724,7 +1724,7 @@ GLModel::Geometry diamond(unsigned short resolution)
#if ENABLE_GLBEGIN_GLEND_REMOVAL
// vertices
for (unsigned short i = 0; i < resolution; ++i) {
- float ii = float(i) * step;
+ const float ii = float(i) * step;
const Vec3f p = { 0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f };
append_vertex(data, p, p.normalized());
}
@@ -1785,5 +1785,73 @@ GLModel::Geometry diamond(unsigned short resolution)
return data;
}
+#if ENABLE_GLBEGIN_GLEND_REMOVAL
+#if ENABLE_SHOW_TOOLPATHS_COG
+GLModel::Geometry smooth_sphere(unsigned short resolution, float radius)
+{
+ resolution = std::max(4, resolution);
+ resolution = std::min(256, resolution); // ensure no unsigned short overflow of indices
+
+ const unsigned short sectorCount = /*2 **/ resolution;
+ const unsigned short stackCount = resolution;
+
+ const float sectorStep = float(2.0 * M_PI / sectorCount);
+ const float stackStep = float(M_PI / stackCount);
+
+ GLModel::Geometry data;
+ data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::USHORT };
+ data.vertices.reserve(((stackCount - 1) * sectorCount + 2) * GLModel::Geometry::vertex_stride_floats(data.format));
+ data.indices.reserve(((2 * (stackCount - 1) * sectorCount) * 3) * GLModel::Geometry::index_stride_bytes(data.format));
+
+ // vertices
+ for (unsigned short i = 0; i <= stackCount; ++i) {
+ // from pi/2 to -pi/2
+ const double stackAngle = 0.5 * M_PI - stackStep * i;
+ const double xy = double(radius) * ::cos(stackAngle);
+ const double z = double(radius) * ::sin(stackAngle);
+ if (i == 0 || i == stackCount) {
+ const Vec3f v(float(xy), 0.0f, float(z));
+ data.add_vertex(v, (Vec3f)v.normalized());
+ }
+ else {
+ for (unsigned short j = 0; j < sectorCount; ++j) {
+ // from 0 to 2pi
+ const double sectorAngle = sectorStep * j;
+ const Vec3f v(float(xy * std::cos(sectorAngle)), float(xy * std::sin(sectorAngle)), float(z));
+ data.add_vertex(v, (Vec3f)v.normalized());
+ }
+ }
+ }
+
+ // triangles
+ for (unsigned short i = 0; i < stackCount; ++i) {
+ // Beginning of current stack.
+ unsigned short k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
+ const unsigned short k1_first = k1;
+ // Beginning of next stack.
+ unsigned short k2 = (i == 0) ? 1 : (k1 + sectorCount);
+ const unsigned short k2_first = k2;
+ for (unsigned short j = 0; j < sectorCount; ++j) {
+ // 2 triangles per sector excluding first and last stacks
+ unsigned short k1_next = k1;
+ unsigned short k2_next = k2;
+ if (i != 0) {
+ k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
+ data.add_ushort_triangle(k1, k2, k1_next);
+ }
+ if (i + 1 != stackCount) {
+ k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
+ data.add_ushort_triangle(k1_next, k2, k2_next);
+ }
+ k1 = k1_next;
+ k2 = k2_next;
+ }
+ }
+
+ return data;
+}
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
+
} // namespace GUI
} // namespace Slic3r
diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp
index c02f5186c..f83009412 100644
--- a/src/slic3r/GUI/GLModel.hpp
+++ b/src/slic3r/GUI/GLModel.hpp
@@ -256,6 +256,14 @@ namespace GUI {
// the diamond is contained into a box with size [1, 1, 1]
GLModel::Geometry diamond(unsigned short resolution);
+#if ENABLE_GLBEGIN_GLEND_REMOVAL
+#if ENABLE_SHOW_TOOLPATHS_COG
+ // create a sphere with the given resolution and smooth normals
+ // the origin of the sphere is in its center
+ GLModel::Geometry smooth_sphere(unsigned short resolution, float radius);
+#endif // ENABLE_SHOW_TOOLPATHS_COG
+#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
+
} // namespace GUI
} // namespace Slic3r
diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp
index ae71e90af..33ac9b6bc 100644
--- a/src/slic3r/GUI/GLShadersManager.cpp
+++ b/src/slic3r/GUI/GLShadersManager.cpp
@@ -41,6 +41,10 @@ std::pair GLShadersManager::init()
// used to render 3D scene background
valid &= append_shader("background", { "background.vs", "background.fs" });
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
+#if ENABLE_SHOW_TOOLPATHS_COG
+ // used to render toolpaths center of gravity
+ valid &= append_shader("toolpaths_cog", { "toolpaths_cog.vs", "toolpaths_cog.fs" });
+#endif // ENABLE_SHOW_TOOLPATHS_COG
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
// used to render printbed
diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp
index d9c32e829..97f8edde3 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -126,6 +126,9 @@ public:
ColorChanges,
PausePrints,
CustomGCodes,
+#if ENABLE_SHOW_TOOLPATHS_COG
+ CenterOfGravity,
+#endif // ENABLE_SHOW_TOOLPATHS_COG
Shells,
ToolMarker,
#if !ENABLE_PREVIEW_LAYOUT
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 4616eee85..e70c1111b 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -64,6 +64,9 @@ static const std::map font_icons = {
{ImGui::LegendColorChanges , "legend_colorchanges" },
{ImGui::LegendPausePrints , "legend_pauseprints" },
{ImGui::LegendCustomGCodes , "legend_customgcodes" },
+#if ENABLE_SHOW_TOOLPATHS_COG
+ {ImGui::LegendCOG , "legend_cog" },
+#endif // ENABLE_SHOW_TOOLPATHS_COG
{ImGui::LegendShells , "legend_shells" },
{ImGui::LegendToolMarker , "legend_toolmarker" },
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index c680f6e09..3ef0fcdd9 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -1276,6 +1276,14 @@ void Selection::render_center(bool gizmo_is_dragging)
if (!m_valid || is_empty())
return;
+#if ENABLE_GLBEGIN_GLEND_REMOVAL
+ GLShaderProgram* shader = wxGetApp().get_shader("flat");
+ if (shader == nullptr)
+ return;
+
+ shader->start_using();
+#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
+
const Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center();
glsafe(::glDisable(GL_DEPTH_TEST));
@@ -1284,19 +1292,17 @@ void Selection::render_center(bool gizmo_is_dragging)
glsafe(::glTranslated(center.x(), center.y(), center.z()));
#if ENABLE_GLBEGIN_GLEND_REMOVAL
- GLShaderProgram* shader = wxGetApp().get_shader("flat");
- if (shader == nullptr)
- return;
-
- shader->start_using();
-#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
+ m_vbo_sphere.set_color(ColorRGBA::WHITE());
+#else
m_vbo_sphere.set_color(-1, ColorRGBA::WHITE());
+#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
m_vbo_sphere.render();
+
+ glsafe(::glPopMatrix());
+
#if ENABLE_GLBEGIN_GLEND_REMOVAL
shader->stop_using();
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
-
- glsafe(::glPopMatrix());
}
#endif // ENABLE_RENDER_SELECTION_CENTER