Files
OrcaSlicer/src/slic3r/GUI/GCodeViewer.cpp
T
Andrew Sun c92328c9cc Port libvgcode from PrusaSlicer 2.8.0
Includes (but is not limited to) the following commits:

SPE-2218 - libvgcode - Fixed color of wipe moves for speed range view
SPE-2218 - libvgcode - Fixed detection of fan speed range
SPE-2218 - libvgcode - Fixed detection of temperature range
SPE-2218 - libvgcode - Fixed colors for Actual volumetric flow rate view
SPE-2214 - Fixed detection of toolpaths bounding box in GCodeViewer
SPE-2206 - Modified LibBGCode.cmake to get latest version of libbgcode which fixed parsing of gcode lines G4
libvgcode - Fixed potential out of bound access in ViewerImpl::update_view_full_range()
Tech ENABLE_GL_CORE_PROFILE set as default
Tech ENABLE_OPENGL_ES replaced by build option SLIC3R_OPENGL_ES
libvgcode - Precompiler definition of ENABLE_OPENGL_ES moved into CMakeLists.txt
Added missing include
libvgcode - Textures setup modified to work when building using emscripten
libvgcode - small optimization
libvgcode - fixed OpenGLWrapper::unload_opengl()
libvgcode - CMakeLists.txt modified to work with emscripten
libvgcode - Replace 'glVertexAttribIPointer()' with 'glVertexAttribPointer()' in SegmentTemplate::init() for OpenGL ES
libvgcode - Replace 'xor' with '^' Bitset.hpp
libvgcode - Newer glad library for OpenGL 4.6 and OpenGL ES 3.0
libvgcode - Alternate fix in method ViewerImpl::update_heights_widths() for OpenGL ES
libvgcode - Fixes in glAssertRecentCallImpl()
libvgcode - Fixes in method ViewerImpl::update_heights_widths() for OpenGL ES
Fixed ES shaders so they work with OpenGL ES 3.0
libvgcode - Use multiple plain textures in place of texture buffers for OpenGL ES
libvgcode - Use plain textures in place of texture buffers for OpenGL ES (partial implementation using one texture per buffer)
libvgcode - refactoring of class OpenGLWrapper
libvgcode - small refactoring in shaders
libvgcode - replacement of glMapBuffer() call for OpenGL ES
Fixed warning
libvgcode - Changes into CMakeLists.txt
Fixed debug export of gcode data to be configuration indipendent
Disabled tech ENABLE_NEW_GCODE_VIEWER_DEBUG
Removed obsolete tech ENABLE_GCODE_VIEWER_DATA_CHECKING
Code cleanup and techs removal - completed
Code cleanup and techs removal - step 1
SPE-1872: Implemented G2/G3 lines discretization for gcfMarlinFirmware firmware flavour
SPE-1872: Corrections into GCodeProcessor::process_G2_G3() to match firmware code
SPE-1872: Actual speed profile - Further enhancements of imgui debug window
SPE-1872: Actual speed profile - Rework in its calculation + enhanced imgui debug window
SPE-1872: New imgui widget to show actual speed profile
SPE-1872: Fixed actual speed for seam moves and at extrusion/travel/wipe start
SPE-1872: Fixed rendering of wipe moves when actual speed view is selected
SPE-1872: Actual speed profile extended to travel and wipe moves
SPE-1872: Fixes in function recalculate_trapezoids() and method GCodeProcessor::TimeMachine::calculate_time() to smooth actual speed profile
SPE-1872: Added debug graphic to show move actual speed profile
SPE-1872: libvgcode library: replace volumetric flow rate data with mm3_per_mm to reduce memory usage
SPE-1872: Added visualization of actual volumetric flow rate
SPE-1872: Fixes in calculating actual speed
SPE-1872: Added visualization of actual speed in gcode preview
SPE-2124: Added command line option 'opengl-aa' to allow the user to turn on the automatic selection of max number of supported samples for OpenGL antialising
#12117: Reduced moire patterns by using the highest number of samples available for multisampling
New gcode visualization integration - Partially enabled imgui debug window
New gcode visualization integration - Fixed center of gravity calculation and rendering
New gcode visualization library - Interface for estimated times
New gcode visualization library - Tool marker rendering
New gcode visualization library - Axes aligned bounding boxes calculation
Removed obsolete debug code
New gcode visualization library - Added statistic of used memory
New gcode visualization library - Separation of tool colors and color changes colors to simplify client code
Added missing include
New gcode visualization library - Added print color changes detection
New gcode visualization library - Modified OpenGL ES context detection
New gcode visualization library - Another makefile cleanup
New gcode visualization library - Makefiles cleanup
New gcode visualization library - Added suppression of error 'This function or variable may be unsafe' in VS2022
New gcode visualization library - Compatibility with OpenGL ES
New gcode visualization library - Interface cleanup and documentation
New gcode visualization library - Extended interface to give access to estimated times
New gcode visualization integration - Toggling of top layer only view state
New gcode visualization integration - Removed imperial units from tool position data
Small refactoring
New gcode visualization library - Custom values for travel and wipe moves radius
New gcode visualization library - Allow customization of range colors
New gcode visualization library - Partial update of interface comments/documentation
New gcode visualization integration - Follow-up of 35ee55e29bb231fd01a2eb71ae293832a37ca65d - Better fix for toolpaths visible range when toggling options' visibility
New gcode visualization integration - Fixed toolpaths reset
New gcode visualization library - Fixed method set_option_color()
New gcode visualization library - Fixed method ViewerImpl::set_extrusion_role_color()
New gcode visualization library - Added methods to release gpu resources on demand.
New gcode visualization library - Travel and wipe moves as options
New gcode visualization integration - Fixed toolpaths visible range when toggling options' visibility
New gcode visualization integration - Fixed management of gcode preview view type
New gcode visualization - Fixed wrong include
New gcode visualization - Added missing headers
New gcode visualization - Refactoring + added missing headers
New gcode visualization - New code set as standalone library + embed glad library to load OpenGL functions
New gcode visualization - Fixed errors and warnings when building the new code as a standalone library
New gcode visualization integration - Fixed layers ordering in pre-gcode preview
New gcode visualization integration - Fixed objects' tool colors in pre-gcode preview
Code cleanup
New gcode visualization integration - Tool position properties data window
New gcode visualization integration - Fixed in export toolpaths to obj
New gcode visualization - Inlining in source code
Refactoring
New gcode visualization integration - Export toolpaths to obj
Some refactoring and warning fix
New gcode visualization integration - Customizable travel moves colors
New gcode visualization integration - Customizable options colors
New gcode visualization integration - Customizable extrusion roles colors
New gcode visualization integration - Fixed pre-gcode preview layers times
New gcode visualization integration - Modify pre-gcode preview to use the new toolpaths renderer, objects WIP
New gcode visualization - Modify pre-gcode preview to use the new toolpaths renderer, WIP (brim/skirt/wipe tower)
New gcode visualization integration - Do not reset visible range when toggling options/roles visibility
New gcode visualization - Fixed color of first vertex of top layer (when top layer only option is enabled)
New gcode visualization - Customizable travels and wipes segment radius
New gcode visualization integration - Removed tech ENABLE_GCODE_VIEWER_STATISTICS
New gcode visualization integration - Added check of OpenGL version
New gcode visualization integration - Removed GCodeProcessorResult::spiral_vase_layers
Another bunch of warnings fixes
Fixed warnings
New gcode visualization integration - Removal of old visualization
Fixed includes
New gcode visualization integration - File structure of new code separated in api + src
New gcode visualization integration - View ranges management moved to new visualizer
New gcode visualization integration - Fixed top layer only visualization for MMU printers
New gcode visualization integration - Removed dependency on imgui from new visualizer
Some refactoring
New gcode visualization integration - Removed dependency on Slic3r::GCodeProcessorResult from new visualizer
New gcode visualization integration - Moves' data conversion moved to client side
New gcode visualization: layers times refactoring
A bunch of fixes for the new gcode visualization
New gcode visualization: render in gray color layers containing pause print or custom gcode options when in Color Print view
New gcode visualization integration - Tool colors
New gcode visualization integration - Layers times
New gcode visualization integration - Travels and Extrusion roles times
Fixed detection of start/end of contiguous extrusion paths
New gcode visualization integration - Extrusion roles
New gcode visualization integration - Colors
New gcode visualization integration - Tool position
Center of gravity and tool marker may both be rendered with fixed screen size and a scaling factor
Fixed rendering of options in new gcode visualization
Tool marker NOT rendered by the new visualization code
Center of gravity marker NOT rendered by the new visualization code
Fixed toolpaths_cog shaders
Tool position window for new gcode visualization
Top layer only coloring for neww gcode visualization
Refactoring in preview's new visualization
Hidden imgui debug dialog for new visualization in preview
Synchronization of moves between old and new visualization
Fixed missing gcode window in new visualization
Rendering of debug imgui dialog moved from class libvgcode::Viewer to class libvgcode::Toolpaths + warnings fixing
Some functionality moved from class libvgcode::Viewer to class libvgcode::Toolpaths
Some refactoring and cleanup
Refatoring of PathVertex and Toolpaths::load()
SPE-1982: Tech ENABLE_NEW_GCODE_VIEWER - 1st installment of new toolpaths rendering code (WIP)
2025-09-24 22:49:14 -04:00

4382 lines
212 KiB
C++

#include "libslic3r/libslic3r.h"
#include "GCodeViewer.hpp"
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/PresetBundle.hpp"
//BBS: add convex hull logic for toolpath check
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
#include "Plater.hpp"
#include "Camera.hpp"
#include "I18N.hpp"
#include "GUI_Utils.hpp"
#include "GUI.hpp"
#include "GLCanvas3D.hpp"
#include "GLToolbar.hpp"
#include "GUI_Preview.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Layer.hpp"
#include "Widgets/ProgressDialog.hpp"
#include "MsgDialog.hpp"
#if ENABLE_ACTUAL_SPEED_DEBUG
#define IMGUI_DEFINE_MATH_OPERATORS
#endif // ENABLE_ACTUAL_SPEED_DEBUG
#include <imgui/imgui_internal.h>
#include <GL/glew.h>
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/fstream.hpp>
#include <wx/progdlg.h>
#include <wx/numformatter.h>
#include <array>
#include <algorithm>
#include <chrono>
namespace Slic3r {
namespace GUI {
//BBS translation of EViewType
//const std::string EViewType_Map[(int) GCodeViewer::EViewType::Count] = {
// _u8L("Line Type"),
// _u8L("Layer Height"),
// _u8L("Line Width"),
// _u8L("Speed"),
// _u8L("Fan Speed"),
// _u8L("Temperature"),
// _u8L("Flow"),
// _u8L("Tool"),
// _u8L("Filament")
// };
static std::string get_view_type_string(libvgcode::EViewType view_type)
{
if (view_type == libvgcode::EViewType::FeatureType)
return _u8L("Line Type");
else if (view_type == libvgcode::EViewType::Height)
return _u8L("Layer Height");
else if (view_type == libvgcode::EViewType::Width)
return _u8L("Line Width");
else if (view_type == libvgcode::EViewType::Speed)
return _u8L("Speed");
else if (view_type == libvgcode::EViewType::ActualSpeed)
return _u8L("Actual Speed");
else if (view_type == libvgcode::EViewType::FanSpeed)
return _u8L("Fan Speed");
else if (view_type == libvgcode::EViewType::Temperature)
return _u8L("Temperature");
else if (view_type == libvgcode::EViewType::VolumetricFlowRate)
return _u8L("Flow");
else if (view_type == libvgcode::EViewType::ActualVolumetricFlowRate)
return _u8L("Actual Flow");
else if (view_type == libvgcode::EViewType::Tool)
return _u8L("Tool");
else if (view_type == libvgcode::EViewType::ColorPrint)
return _u8L("Filament");
else if (view_type == libvgcode::EViewType::LayerTimeLinear)
return _u8L("Layer Time");
else if (view_type == libvgcode::EViewType::LayerTimeLogarithmic)
return _u8L("Layer Time (log)");
return "";
}
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
// Returns -1 if there is no such member.
static int find_close_layer_idx(const std::vector<double> &zs, double &z, double eps)
{
if (zs.empty()) return -1;
auto it_h = std::lower_bound(zs.begin(), zs.end(), z);
if (it_h == zs.end()) {
auto it_l = it_h;
--it_l;
if (z - *it_l < eps) return int(zs.size() - 1);
} else if (it_h == zs.begin()) {
if (*it_h - z < eps) return 0;
} else {
auto it_l = it_h;
--it_l;
double dist_l = z - *it_l;
double dist_h = *it_h - z;
if (std::min(dist_l, dist_h) < eps) { return (dist_l < dist_h) ? int(it_l - zs.begin()) : int(it_h - zs.begin()); }
}
return -1;
}
#if ENABLE_ACTUAL_SPEED_DEBUG
int GCodeViewer::SequentialView::ActualSpeedImguiWidget::plot(const char* label, const std::array<float, 2>& frame_size)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return -1;
const ImGuiStyle& style = ImGui::GetStyle();
const ImGuiIO& io = ImGui::GetIO();
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true);
ImVec2 internal_frame_size(frame_size[0], frame_size[1]);
internal_frame_size = ImGui::CalcItemSize(internal_frame_size, ImGui::CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + internal_frame_size);
const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, 0, &frame_bb))
return -1;
const bool hovered = ImGui::ItemHoverable(frame_bb, id);
ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
static const int values_count_min = 2;
const int values_count = static_cast<int>(data.size());
int idx_hovered = -1;
const ImVec2 offset(10.0f, 0.0f);
const float size_y = y_range.second - y_range.first;
const float size_x = data.back().pos - data.front().pos;
if (size_x > 0.0f && values_count >= values_count_min) {
const float inv_scale_y = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
const float inv_scale_x = 1.0f / size_x;
const float x0 = data.front().pos;
const float y0 = y_range.first;
const ImU32 grid_main_color = ImGui::GetColorU32(ImVec4(0.5f, 0.5f, 0.5f, 0.5f));
const ImU32 grid_secondary_color = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.5f, 0.5f));
// horizontal levels
for (const auto& [level, color] : levels) {
const float y = 1.0f - ImSaturate((level - y_range.first) * inv_scale_y);
window->DrawList->AddLine(ImLerp(inner_bb.Min, ImVec2(inner_bb.Min.x + offset.x, inner_bb.Max.y), ImVec2(0.1f, y)),
ImLerp(inner_bb.Min, ImVec2(inner_bb.Min.x + offset.x, inner_bb.Max.y), ImVec2(0.9f, y)), ImGuiWrapper::to_ImU32(color), 3.0f);
}
// vertical positions
for (int n = 0; n < values_count - 1; ++n) {
const float x = ImSaturate((data[n].pos - x0) * inv_scale_x);
window->DrawList->AddLine(ImLerp(inner_bb.Min + offset, inner_bb.Max, ImVec2(x, 0.0f)),
ImLerp(inner_bb.Min + offset, inner_bb.Max, ImVec2(x, 1.0f)), data[n].internal ? grid_secondary_color : grid_main_color);
}
window->DrawList->AddLine(ImLerp(inner_bb.Min + offset, inner_bb.Max, ImVec2(1.0f, 0.0f)),
ImLerp(inner_bb.Min + offset, inner_bb.Max, ImVec2(1.0f, 1.0f)), grid_main_color);
// profiile
const ImU32 col_base = ImGui::GetColorU32(ImVec4(0.8f, 0.8f, 0.8f, 1.0f));
const ImU32 col_hovered = ImGui::GetColorU32(ImGuiCol_PlotLinesHovered);
for (int n = 0; n < values_count - 1; ++n) {
const ImVec2 tp1(ImSaturate((data[n].pos - x0) * inv_scale_x), 1.0f - ImSaturate((data[n].speed - y0) * inv_scale_y));
const ImVec2 tp2(ImSaturate((data[n + 1].pos - x0) * inv_scale_x), 1.0f - ImSaturate((data[n + 1].speed - y0) * inv_scale_y));
// Tooltip on hover
if (hovered && inner_bb.Contains(io.MousePos)) {
const float t = ImClamp((io.MousePos.x - inner_bb.Min.x - offset.x) / (inner_bb.Max.x - inner_bb.Min.x - offset.x), 0.0f, 0.9999f);
if (tp1.x < t && t < tp2.x)
idx_hovered = n;
}
window->DrawList->AddLine(ImLerp(inner_bb.Min + offset, inner_bb.Max, tp1), ImLerp(inner_bb.Min + offset, inner_bb.Max, tp2),
idx_hovered == n ? col_hovered : col_base, 2.0f);
}
}
if (label_size.x > 0.0f)
ImGui::RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
return idx_hovered;
}
#endif // ENABLE_ACTUAL_SPEED_DEBUG
// float GCodeViewer::Extrusions::Range::get_value_at_step(int step) const {
// if (!log_scale)
// return min + static_cast<float>(step) * step_size();
// else
// return std::exp(std::log(min) + static_cast<float>(step) * step_size());
//
// }
void GCodeViewer::SequentialView::Marker::init(std::string filename)
{
if (filename.empty()) {
m_model.init_from(stilized_arrow(16, 1.5f, 3.0f, 0.8f, 3.0f));
} else {
m_model.init_from_file(filename);
}
m_model.set_color({ 1.0f, 1.0f, 1.0f, 0.5f });
}
void GCodeViewer::SequentialView::Marker::update_curr_move(const GCodeProcessorResult::MoveVertex move) {
m_curr_move = move;
}
//BBS: GUI refactor: add canvas size from parameters
void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_height, const libvgcode::EViewType& view_type)
{
if (!m_visible)
return;
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
shader->start_using();
shader->set_uniform("emission_factor", 0.0f);
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix();
float scale_factor = m_scale_factor;
//if (m_fixed_screen_size)
// scale_factor *= 10.0f * camera.get_inv_zoom(); // TODO
const Transform3d model_matrix = (Geometry::translation_transform((m_world_position + m_model_z_offset * Vec3f::UnitZ()).cast<double>()) *
Geometry::translation_transform(scale_factor * m_model.get_bounding_box().size().z() * Vec3d::UnitZ()) * Geometry::rotation_transform({ M_PI, 0.0, 0.0 })) *
Geometry::scale_transform(scale_factor);
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
m_model.render();
shader->stop_using();
glsafe(::glDisable(GL_BLEND));
static float last_window_width = 0.0f;
size_t text_line = 0;
static size_t last_text_line = 0;
const ImU32 text_name_clr = m_is_dark ? IM_COL32(255, 255, 255, 0.88 * 255) : IM_COL32(38, 46, 48, 255);
const ImU32 text_value_clr = m_is_dark ? IM_COL32(255, 255, 255, 0.4 * 255) : IM_COL32(144, 144, 144, 255);
ImGuiWrapper& imgui = *wxGetApp().imgui();
//BBS: GUI refactor: add canvas size from parameters
imgui.set_next_window_pos(0.5f * static_cast<float>(canvas_width), static_cast<float>(canvas_height), ImGuiCond_Always, 0.5f, 1.0f);
imgui.push_toolbar_style(m_scale);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0, 4.0 * m_scale));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0 * m_scale, 6.0 * m_scale));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, text_name_clr);
ImGui::PushStyleColor(ImGuiCol_Text, text_value_clr);
imgui.begin(std::string("ExtruderPosition"), ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar);
ImGui::AlignTextToFramePadding();
//BBS: minus the plate offset when show tool position
PartPlateList& partplate_list = wxGetApp().plater()->get_partplate_list();
PartPlate* plate = partplate_list.get_curr_plate();
const Vec3f position = m_world_position + m_world_offset;
std::string x = ImGui::ColorMarkerStart + std::string("X: ") + ImGui::ColorMarkerEnd;
std::string y = ImGui::ColorMarkerStart + std::string("Y: ") + ImGui::ColorMarkerEnd;
std::string z = ImGui::ColorMarkerStart + std::string("Z: ") + ImGui::ColorMarkerEnd;
std::string height = ImGui::ColorMarkerStart + _u8L("Height: ") + ImGui::ColorMarkerEnd;
std::string width = ImGui::ColorMarkerStart + _u8L("Width: ") + ImGui::ColorMarkerEnd;
std::string speed = ImGui::ColorMarkerStart + _u8L("Speed: ") + ImGui::ColorMarkerEnd;
std::string flow = ImGui::ColorMarkerStart + _u8L("Flow: ") + ImGui::ColorMarkerEnd;
std::string layer_time = ImGui::ColorMarkerStart + _u8L("Layer Time: ") + ImGui::ColorMarkerEnd;
std::string fanspeed = ImGui::ColorMarkerStart + _u8L("Fan: ") + ImGui::ColorMarkerEnd;
std::string temperature = ImGui::ColorMarkerStart + _u8L("Temperature: ") + ImGui::ColorMarkerEnd;
const float item_size = imgui.calc_text_size(std::string_view{"X: 000.000 "}).x;
const float item_spacing = imgui.get_item_spacing().x;
const float window_padding = ImGui::GetStyle().WindowPadding.x;
char buf[1024];
if (true)
{
float startx2 = window_padding + item_size + item_spacing;
float startx3 = window_padding + 2*(item_size + item_spacing);
sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x());
ImGui::PushItemWidth(item_size);
imgui.text(buf);
ImGui::SameLine(startx2);
sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y());
ImGui::PushItemWidth(item_size);
imgui.text(buf);
ImGui::SameLine(startx3);
sprintf(buf, "%s%.3f", z.c_str(), position.z());
ImGui::PushItemWidth(item_size);
imgui.text(buf);
sprintf(buf, "%s%.0f", speed.c_str(), m_curr_move.feedrate);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
switch (view_type) {
case libvgcode::EViewType::Height: {
ImGui::SameLine(startx2);
sprintf(buf, "%s%.2f", height.c_str(), m_curr_move.height);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
break;
}
case libvgcode::EViewType::Width: {
ImGui::SameLine(startx2);
sprintf(buf, "%s%.2f", width.c_str(), m_curr_move.width);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
break;
}
// case EViewType::Feedrate: {
// ImGui::SameLine(startx2);
// sprintf(buf, "%s%.0f", speed.c_str(), m_curr_move.feedrate);
// ImGui::PushItemWidth(item_size);
// imgui.text(buf);
// break;
// }
case libvgcode::EViewType::VolumetricFlowRate: {
if (m_curr_move.type != EMoveType::Extrude) break;
ImGui::SameLine(startx2);
sprintf(buf, "%s%.2f", flow.c_str(), m_curr_move.volumetric_rate());
ImGui::PushItemWidth(item_size);
imgui.text(buf);
break;
}
case libvgcode::EViewType::FanSpeed: {
ImGui::SameLine(startx2);
sprintf(buf, "%s%.0f", fanspeed.c_str(), m_curr_move.fan_speed);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
break;
}
case libvgcode::EViewType::Temperature: {
ImGui::SameLine(startx2);
sprintf(buf, "%s%.0f", temperature.c_str(), m_curr_move.temperature);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
break;
}
case libvgcode::EViewType::LayerTimeLinear:
case libvgcode::EViewType::LayerTimeLogarithmic: {
ImGui::SameLine(startx2);
sprintf(buf, "%s%.1f", layer_time.c_str(), m_curr_move.layer_duration);
ImGui::PushItemWidth(item_size);
imgui.text(buf);
break;
}
default:
break;
}
text_line = 2;
}
// else {
// sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x());
// imgui.text(buf);
// ImGui::SameLine();
// sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y());
// imgui.text(buf);
// ImGui::SameLine();
// sprintf(buf, "%s%.3f", z.c_str(), position.z());
// imgui.text(buf);
// text_line = 1;
// }
// force extra frame to automatically update window size
float window_width = ImGui::GetWindowWidth();
if (window_width != last_window_width || text_line != last_text_line) {
last_window_width = window_width;
last_text_line = text_line;
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
imgui.set_requires_extra_frame();
#else
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
}
imgui.end();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(2);
imgui.pop_toolbar_style();
}
static std::string to_string(libvgcode::EMoveType type)
{
switch (type)
{
case libvgcode::EMoveType::Noop: { return _u8L("Noop"); }
case libvgcode::EMoveType::Retract: { return _u8L("Retract"); }
case libvgcode::EMoveType::Unretract: { return _u8L("Unretract"); }
case libvgcode::EMoveType::Seam: { return _u8L("Seam"); }
case libvgcode::EMoveType::ToolChange: { return _u8L("Tool Change"); }
case libvgcode::EMoveType::ColorChange: { return _u8L("Color Change"); }
case libvgcode::EMoveType::PausePrint: { return _u8L("Pause Print"); }
case libvgcode::EMoveType::CustomGCode: { return _u8L("Custom GCode"); }
case libvgcode::EMoveType::Travel: { return _u8L("Travel"); }
case libvgcode::EMoveType::Wipe: { return _u8L("Wipe"); }
case libvgcode::EMoveType::Extrude: { return _u8L("Extrude"); }
default: { return _u8L("Unknown"); }
}
}
static std::string to_string(libvgcode::EGCodeExtrusionRole role)
{
switch (role)
{
case libvgcode::EGCodeExtrusionRole::None: { return _u8L("Unknown"); }
case libvgcode::EGCodeExtrusionRole::Perimeter: { return _u8L("Perimeter"); }
case libvgcode::EGCodeExtrusionRole::ExternalPerimeter: { return _u8L("External perimeter"); }
case libvgcode::EGCodeExtrusionRole::OverhangPerimeter: { return _u8L("Overhang perimeter"); }
case libvgcode::EGCodeExtrusionRole::InternalInfill: { return _u8L("Internal infill"); }
case libvgcode::EGCodeExtrusionRole::SolidInfill: { return _u8L("Solid infill"); }
case libvgcode::EGCodeExtrusionRole::TopSolidInfill: { return _u8L("Top solid infill"); }
case libvgcode::EGCodeExtrusionRole::Ironing: { return _u8L("Ironing"); }
case libvgcode::EGCodeExtrusionRole::BridgeInfill: { return _u8L("Bridge infill"); }
case libvgcode::EGCodeExtrusionRole::GapFill: { return _u8L("Gap fill"); }
case libvgcode::EGCodeExtrusionRole::Skirt: { return _u8L("Skirt/Brim"); }
case libvgcode::EGCodeExtrusionRole::SupportMaterial: { return _u8L("Support material"); }
case libvgcode::EGCodeExtrusionRole::SupportMaterialInterface: { return _u8L("Support material interface"); }
case libvgcode::EGCodeExtrusionRole::WipeTower: { return _u8L("Wipe tower"); }
case libvgcode::EGCodeExtrusionRole::Custom: { return _u8L("Custom"); }
default: { return _u8L("Unknown"); }
}
}
void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode::Viewer* viewer)
{
static float last_window_width = 0.0f;
static size_t last_text_length = 0;
static bool properties_shown = false;
if (viewer != nullptr) {
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<float>(cnv_size.get_width()), static_cast<float>(cnv_size.get_height()), ImGuiCond_Always, 0.5f, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::SetNextWindowBgAlpha(0.25f);
imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
ImGui::AlignTextToFramePadding();
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Position") + ":");
ImGui::SameLine();
libvgcode::PathVertex vertex = viewer->get_current_vertex();
size_t vertex_id = viewer->get_current_vertex_id();
if (vertex.type == libvgcode::EMoveType::Seam) {
vertex_id = static_cast<size_t>(viewer->get_view_visible_range()[1]) - 1;
vertex = viewer->get_vertex_at(vertex_id);
}
char buf[1024];
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", vertex.position[0], vertex.position[1], vertex.position[2]);
ImGuiWrapper::text(std::string(buf));
ImGui::SameLine();
if (imgui.image_button(properties_shown ? ImGui::HorizontalHide : ImGui::HorizontalShow, properties_shown ? _u8L("Hide properties") : _u8L("Show properties"))) {
properties_shown = !properties_shown;
imgui.requires_extra_frame();
}
if (properties_shown) {
auto append_table_row = [](const std::string& label, std::function<void(void)> value_callback) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label);
ImGui::TableSetColumnIndex(1);
value_callback();
};
ImGui::Separator();
if (ImGui::BeginTable("Properties", 2)) {
char buff[1024];
append_table_row(_u8L("Type"), [&vertex]() {
std::string text = _u8L(to_string(vertex.type));
if (vertex.is_extrusion())
text += " (" + _u8L(to_string(vertex.role)) + ")";
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Width") + " (" + _u8L("mm") + ")", [&vertex, &buff]() {
std::string text;
if (vertex.is_extrusion()) {
sprintf(buff, "%.3f", vertex.width);
text = std::string(buff);
}
else
text = _u8L("N/A");
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Height") + " (" + _u8L("mm") + ")", [&vertex, &buff]() {
std::string text;
if (vertex.is_extrusion()) {
sprintf(buff, "%.3f", vertex.height);
text = std::string(buff);
}
else
text = _u8L("N/A");
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Layer"), [&vertex, &buff]() {
sprintf(buff, "%d", vertex.layer_id + 1);
const std::string text = std::string(buff);
ImGuiWrapper::text(text);
});
#if !ENABLE_ACTUAL_SPEED_DEBUG
append_table_row(_u8L("Speed") + " (" + _u8L("mm/s") + ")", [&vertex, &buff]() {
std::string text;
if (vertex.is_extrusion()) {
sprintf(buff, "%.1f", vertex.feedrate);
text = std::string(buff);
}
else
text = _u8L("N/A");
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Actual speed") + " (" + _u8L("mm/s") + ")", [&vertex, &buff]() {
std::string text;
if (vertex.is_extrusion()) {
sprintf(buff, "%.1f", vertex.actual_feedrate);
text = std::string(buff);
}
else
text = _u8L("N/A");
ImGuiWrapper::text(text);
});
#endif // !ENABLE_ACTUAL_SPEED_DEBUG
append_table_row(_u8L("Fan speed") + " (" + _u8L("%") + ")", [&vertex, &buff]() {
std::string text;
if (vertex.is_extrusion()) {
sprintf(buff, "%.0f", vertex.fan_speed);
text = std::string(buff);
}
else
text = _u8L("N/A");
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Temperature") + " (" + _u8L("°C") + ")", [&vertex, &buff]() {
sprintf(buff, "%.0f", vertex.temperature);
ImGuiWrapper::text(std::string(buff));
});
append_table_row(_u8L("Time"), [viewer, &vertex, &buff, vertex_id]() {
const float estimated_time = viewer->get_estimated_time_at(vertex_id);
sprintf(buff, "%s (%.3fs)", get_time_dhms(estimated_time).c_str(), vertex.times[static_cast<size_t>(viewer->get_time_mode())]);
const std::string text = std::string(buff);
ImGuiWrapper::text(text);
});
ImGui::EndTable();
}
#if ENABLE_ACTUAL_SPEED_DEBUG
if (vertex.is_extrusion() || vertex.is_travel() || vertex.is_wipe()) {
ImGui::Spacing();
ImGuiWrapper::text(_u8L("Actual speed profile"));
ImGui::SameLine();
static bool table_shown = false;
if (imgui.button(table_shown ? _u8L("Hide table") : _u8L("Show table")))
table_shown = !table_shown;
ImGui::Separator();
const int hover_id = m_actual_speed_imgui_widget.plot("##ActualSpeedProfile", { -1.0f, 150.0f });
if (table_shown) {
static float table_wnd_height = 0.0f;
const ImVec2 wnd_size = ImGui::GetWindowSize();
imgui.set_next_window_pos(ImGui::GetWindowPos().x + wnd_size.x, static_cast<float>(cnv_size.get_height()), ImGuiCond_Always, 0.0f, 1.0f);
ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, wnd_size.y });
imgui.begin(std::string("ToolPositionTableWnd"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
if (ImGui::BeginTable("ToolPositionTable", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY)) {
char buff[1024];
ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
ImGui::TableSetupColumn("Position (mm)");
ImGui::TableSetupColumn("Speed (mm/s)");
ImGui::TableHeadersRow();
int counter = 0;
for (const ActualSpeedImguiWidget::Item& item : m_actual_speed_imgui_widget.data) {
const bool highlight = hover_id >= 0 && (counter == hover_id || counter == hover_id + 1);
if (highlight && counter == hover_id)
ImGui::SetScrollHereY();
ImGui::TableNextRow();
const ImU32 row_bg_color = ImGui::GetColorU32(item.internal ? ImVec4(0.0f, 0.0f, 0.5f, 0.25f) : ImVec4(0.5f, 0.5f, 0.5f, 0.25f));
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, row_bg_color);
ImGui::TableSetColumnIndex(0);
sprintf(buff, "%.3f", item.pos);
imgui.text_colored(highlight ? ImGuiWrapper::COL_ORANGE_LIGHT : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), buff);
ImGui::TableSetColumnIndex(1);
sprintf(buff, "%.1f", item.speed);
imgui.text_colored(highlight ? ImGuiWrapper::COL_ORANGE_LIGHT : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), buff);
++counter;
}
ImGui::EndTable();
}
const float curr_table_wnd_height = ImGui::GetWindowHeight();
if (table_wnd_height != curr_table_wnd_height) {
table_wnd_height = curr_table_wnd_height;
// require extra frame to hide the table scroll bar (bug in imgui)
imgui.set_requires_extra_frame();
}
imgui.end();
}
}
#endif // ENABLE_ACTUAL_SPEED_DEBUG
}
// 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();
}
else {
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<float>(cnv_size.get_width()), static_cast<float>(cnv_size.get_height()), ImGuiCond_Always, 0.5f, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::SetNextWindowBgAlpha(0.25f);
imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":");
ImGui::SameLine();
char buf[1024];
const Vec3f position = m_world_position + m_world_offset + m_z_offset * Vec3f::UnitZ();
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z());
ImGuiWrapper::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();
}
}
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends)
{
assert(! m_file.is_open());
if (m_file.is_open())
return;
m_filename = filename;
m_lines_ends = lines_ends;
m_selected_line_id = 0;
m_last_lines_size = 0;
try
{
m_file.open(boost::filesystem::path(m_filename));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": mapping file " << m_filename;
}
catch (...)
{
BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window.";
reset();
}
}
//BBS: GUI refactor: move to right
void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, float right, uint64_t curr_line_id) const
//void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const
{
// Orca: truncate long lines(>55 characters), add "..." at the end
auto update_lines = [this](uint64_t start_id, uint64_t end_id) {
std::vector<Line> ret;
ret.reserve(end_id - start_id + 1);
for (uint64_t id = start_id; id <= end_id; ++id) {
// read line from file
const size_t start = id == 1 ? 0 : m_lines_ends[id - 2];
const size_t original_len = m_lines_ends[id - 1] - start;
const size_t len = std::min(original_len, (size_t) 55);
std::string gline(m_file.data() + start, len);
// If original line is longer than 55 characters, truncate and append "..."
if (original_len > 55)
gline = gline.substr(0, 52) + "...";
std::string command, parameters, comment;
// extract comment
std::vector<std::string> tokens;
boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on);
command = tokens.front();
if (tokens.size() > 1)
comment = ";" + tokens.back();
// extract gcode command and parameters
if (!command.empty()) {
boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on);
command = tokens.front();
if (tokens.size() > 1) {
for (size_t i = 1; i < tokens.size(); ++i) {
parameters += " " + tokens[i];
}
}
}
ret.push_back({command, parameters, comment});
}
return ret;
};
static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT;
static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK;
static const ImVec4 COMMAND_COLOR = {0.8f, 0.8f, 0.0f, 1.0f};
static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f };
static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f };
if (!wxGetApp().show_gcode_window() || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0)
return;
// window height
const float wnd_height = bottom - top;
// number of visible lines
const float text_height = ImGui::CalcTextSize("0").y;
const ImGuiStyle& style = ImGui::GetStyle();
const uint64_t lines_count = static_cast<uint64_t>((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y));
if (lines_count == 0)
return;
// visible range
const uint64_t half_lines_count = lines_count / 2;
uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0;
uint64_t end_id = start_id + lines_count - 1;
if (end_id >= static_cast<uint64_t>(m_lines_ends.size())) {
end_id = static_cast<uint64_t>(m_lines_ends.size()) - 1;
start_id = end_id - lines_count + 1;
}
// updates list of lines to show, if needed
if (m_selected_line_id != curr_line_id || m_last_lines_size != end_id - start_id + 1) {
try
{
*const_cast<std::vector<Line>*>(&m_lines) = update_lines(start_id, end_id);
}
catch (...)
{
BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << m_filename << ". Cannot show G-code window.";
return;
}
*const_cast<uint64_t*>(&m_selected_line_id) = curr_line_id;
*const_cast<size_t*>(&m_last_lines_size) = m_lines.size();
}
// line number's column width
const float id_width = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x;
ImGuiWrapper& imgui = *wxGetApp().imgui();
//BBS: GUI refactor: move to right
//imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f);
imgui.set_next_window_pos(right, top + 6 * m_scale, ImGuiCond_Always, 1.0f, 0.0f); // ORCA add a small gap between legend and code viewer
imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f * m_scale); // ORCA add window rounding to modernize / match style
ImGui::SetNextWindowBgAlpha(0.8f);
imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
// center the text in the window by pushing down the first line
const float f_lines_count = static_cast<float>(lines_count);
ImGui::SetCursorPosY(0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y));
// render text lines
for (uint64_t id = start_id; id <= end_id; ++id) {
const Line& line = m_lines[id - start_id];
// rect around the current selected line
if (id == curr_line_id) {
//BBS: GUI refactor: move to right
const float pos_y = ImGui::GetCursorScreenPos().y;
const float pos_x = ImGui::GetCursorScreenPos().x;
const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y;
const float half_ItemSpacing_x = 0.5f * style.ItemSpacing.x;
//ImGui::GetWindowDrawList()->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y },
// { ImGui::GetCurrentWindow()->Size.x - half_padding_x, pos_y + text_height + half_ItemSpacing_y },
// ImGui::GetColorU32(SELECTION_RECT_COLOR));
ImGui::GetWindowDrawList()->AddRect({ pos_x - half_ItemSpacing_x, pos_y - half_ItemSpacing_y },
{ right - half_ItemSpacing_x, pos_y + text_height + half_ItemSpacing_y },
ImGui::GetColorU32(SELECTION_RECT_COLOR));
}
// render line number
const std::string id_str = std::to_string(id);
// spacer to right align text
ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height });
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR);
imgui.text(id_str);
ImGui::PopStyleColor();
if (!line.command.empty() || !line.comment.empty())
ImGui::SameLine();
// render command
if (!line.command.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text, COMMAND_COLOR);
imgui.text(line.command);
ImGui::PopStyleColor();
}
// render parameters
if (!line.parameters.empty()) {
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, PARAMETERS_COLOR);
imgui.text(line.parameters);
ImGui::PopStyleColor();
}
// render comment
if (!line.comment.empty()) {
if (!line.command.empty())
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, COMMENT_COLOR);
imgui.text(line.comment);
ImGui::PopStyleColor();
}
}
imgui.end();
ImGui::PopStyleVar();
}
void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()
{
//BBS: add log to trace the gcode file issue
if (m_file.is_open()) {
m_file.close();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished mapping file " << m_filename;
}
}
void GCodeViewer::SequentialView::render(const bool has_render_path, float legend_height, const libvgcode::Viewer* viewer, uint32_t gcode_id, int canvas_width, int canvas_height, int right_margin, const libvgcode::EViewType& view_type)
{
if (has_render_path && m_show_marker) {
// marker.set_world_position(current_position);
// marker.set_world_offset(current_offset);
marker.render(canvas_width, canvas_height, view_type);
}
//float bottom = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height();
// BBS
#if 0
if (wxGetApp().is_editor())
bottom -= wxGetApp().plater()->get_view_toolbar().get_height();
#endif
if (has_render_path)
gcode_window.render(legend_height + 2, std::max(10.f, (float)canvas_height - 40), (float)canvas_width - (float)right_margin, gcode_id);
}
GCodeViewer::GCodeViewer()
{
m_moves_slider = new IMSlider(0, 0, 0, 100, wxSL_HORIZONTAL);
m_layers_slider = new IMSlider(0, 0, 0, 100, wxSL_VERTICAL);
//m_extrusions.reset_role_visibility_flags(); TODO
}
GCodeViewer::~GCodeViewer()
{
reset();
if (m_moves_slider) {
delete m_moves_slider;
m_moves_slider = nullptr;
}
if (m_layers_slider) {
delete m_layers_slider;
m_layers_slider = nullptr;
}
}
void GCodeViewer::init(ConfigOptionMode mode, PresetBundle* preset_bundle)
{
if (m_gl_data_initialized)
return;
// BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": enter, m_buffers.size=%1%")
// %m_buffers.size();
// initializes tool marker
std::string filename;
if (preset_bundle != nullptr) {
const Preset* curr = &preset_bundle->printers.get_selected_preset();
if (curr->is_system)
filename = PresetUtils::system_printer_hotend_model(*curr);
else {
auto *printer_model = curr->config.opt<ConfigOptionString>("printer_model");
if (printer_model != nullptr && ! printer_model->value.empty()) {
filename = preset_bundle->get_hotend_model_for_printer_model(printer_model->value);
}
if (filename.empty()) {
filename = preset_bundle->get_hotend_model_for_printer_model(PresetBundle::ORCA_DEFAULT_PRINTER_MODEL);
}
}
}
m_sequential_view.marker.init(filename);
// initializes point sizes
std::array<int, 2> point_sizes;
::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_sizes.data());
m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
// BBS initialzed view_type items
m_user_mode = mode;
update_by_mode(m_user_mode);
m_layers_slider->init_texture();
m_gl_data_initialized = true;
try
{
m_viewer.init(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
glcheck();
}
catch (const std::exception& e)
{
MessageDialog msg_dlg(wxGetApp().plater(), e.what(), _L("Error"), wxICON_ERROR | wxOK);
msg_dlg.ShowModal();
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": finished");
}
void GCodeViewer::on_change_color_mode(bool is_dark) {
m_is_dark = is_dark;
m_sequential_view.marker.on_change_color_mode(m_is_dark);
m_sequential_view.gcode_window.on_change_color_mode(m_is_dark);
}
void GCodeViewer::set_scale(float scale)
{
if(m_scale != scale)m_scale = scale;
if (m_sequential_view.m_scale != scale) {
m_sequential_view.m_scale = scale;
m_sequential_view.marker.m_scale = scale;
m_sequential_view.gcode_window.m_scale = scale; // ORCA
}
}
void GCodeViewer::update_by_mode(ConfigOptionMode mode)
{
view_type_items.clear();
view_type_items_str.clear();
options_items.clear();
// BBS initialzed view_type items
view_type_items.push_back(libvgcode::EViewType::FeatureType);
view_type_items.push_back(libvgcode::EViewType::ColorPrint);
view_type_items.push_back(libvgcode::EViewType::Speed);
view_type_items.push_back(libvgcode::EViewType::Height);
view_type_items.push_back(libvgcode::EViewType::Width);
view_type_items.push_back(libvgcode::EViewType::VolumetricFlowRate);
view_type_items.push_back(libvgcode::EViewType::LayerTimeLinear);
view_type_items.push_back(libvgcode::EViewType::LayerTimeLogarithmic);
view_type_items.push_back(libvgcode::EViewType::FanSpeed);
view_type_items.push_back(libvgcode::EViewType::Temperature);
//if (mode == ConfigOptionMode::comDevelop) {
// view_type_items.push_back(EViewType::Tool);
//}
for (int i = 0; i < view_type_items.size(); i++) {
view_type_items_str.push_back(get_view_type_string(view_type_items[i]));
}
// BBS for first layer inspection
view_type_items.push_back(libvgcode::EViewType::Tool);
options_items.push_back(EMoveType::Travel);
options_items.push_back(EMoveType::Retract);
options_items.push_back(EMoveType::Unretract);
options_items.push_back(EMoveType::Wipe);
//if (mode == ConfigOptionMode::comDevelop) {
// options_items.push_back(EMoveType::Tool_change);
//}
//BBS: seam is not real move and extrusion, put at last line
options_items.push_back(EMoveType::Seam);
}
std::vector<int> GCodeViewer::get_plater_extruder()
{
return m_plater_extruder;
}
//BBS: always load shell at preview
void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const Print& print, const std::vector<std::string>& str_tool_colors,
const std::vector<std::string>& str_color_print_colors, const BuildVolume& build_volume,
const std::vector<BoundingBoxf3>& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode)
{
m_loaded_as_preview = false;
const bool current_top_layer_only = m_viewer.is_top_layer_only_view_range();
const bool required_top_layer_only = get_app_config()->get_bool("seq_top_layer_only");
if (current_top_layer_only != required_top_layer_only)
m_viewer.toggle_top_layer_only_view_range();
// avoid processing if called with the same gcode_result
if (m_last_result_id == gcode_result.id && wxGetApp().is_editor()) {
//BBS: add logs
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": the same id %1%, return directly, result %2% ") % m_last_result_id % (&gcode_result);
// collect tool colors
libvgcode::Palette tools_colors;
tools_colors.reserve(str_tool_colors.size());
for (const std::string& color : str_tool_colors) {
tools_colors.emplace_back(libvgcode::convert(color));
}
m_viewer.set_tool_colors(tools_colors);
// collect color print colors
libvgcode::Palette color_print_colors;
color_print_colors.reserve(str_color_print_colors.size());
for (const std::string& color : str_color_print_colors) {
color_print_colors.emplace_back(libvgcode::convert(color));
}
m_viewer.set_color_print_colors(color_print_colors);
return;
}
//BBS: add logs
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": gcode result %1%, new id %2%, gcode file %3% ") % (&gcode_result) % m_last_result_id % gcode_result.filename;
// release gpu memory, if used
reset();
//BBS: add mutex for protection of gcode result
gcode_result.lock();
//BBS: add safe check
if (gcode_result.moves.size() == 0) {
//result cleaned before slicing ,should return here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": gcode result reset before, return directly!");
gcode_result.unlock();
return;
}
// convert data from PrusaSlicer format to libvgcode format
libvgcode::GCodeInputData data = libvgcode::convert(gcode_result, str_tool_colors, str_color_print_colors, m_viewer);
//#define ENABLE_DATA_EXPORT 1
//#if ENABLE_DATA_EXPORT
// auto extrusion_role_to_string = [](libvgcode::EGCodeExtrusionRole role) {
// switch (role) {
// case libvgcode::EGCodeExtrusionRole::None: { return "EGCodeExtrusionRole::None"; }
// case libvgcode::EGCodeExtrusionRole::Perimeter: { return "EGCodeExtrusionRole::Perimeter"; }
// case libvgcode::EGCodeExtrusionRole::ExternalPerimeter: { return "EGCodeExtrusionRole::ExternalPerimeter"; }
// case libvgcode::EGCodeExtrusionRole::OverhangPerimeter: { return "EGCodeExtrusionRole::OverhangPerimeter"; }
// case libvgcode::EGCodeExtrusionRole::InternalInfill: { return "EGCodeExtrusionRole::InternalInfill"; }
// case libvgcode::EGCodeExtrusionRole::SolidInfill: { return "EGCodeExtrusionRole::SolidInfill"; }
// case libvgcode::EGCodeExtrusionRole::TopSolidInfill: { return "EGCodeExtrusionRole::TopSolidInfill"; }
// case libvgcode::EGCodeExtrusionRole::Ironing: { return "EGCodeExtrusionRole::Ironing"; }
// case libvgcode::EGCodeExtrusionRole::BridgeInfill: { return "EGCodeExtrusionRole::BridgeInfill"; }
// case libvgcode::EGCodeExtrusionRole::GapFill: { return "EGCodeExtrusionRole::GapFill"; }
// case libvgcode::EGCodeExtrusionRole::Skirt: { return "EGCodeExtrusionRole::Skirt"; }
// case libvgcode::EGCodeExtrusionRole::SupportMaterial: { return "EGCodeExtrusionRole::SupportMaterial"; }
// case libvgcode::EGCodeExtrusionRole::SupportMaterialInterface: { return "EGCodeExtrusionRole::SupportMaterialInterface"; }
// case libvgcode::EGCodeExtrusionRole::WipeTower: { return "EGCodeExtrusionRole::WipeTower"; }
// case libvgcode::EGCodeExtrusionRole::Custom: { return "EGCodeExtrusionRole::Custom"; }
// case libvgcode::EGCodeExtrusionRole::COUNT: { return "EGCodeExtrusionRole::COUNT"; }
// }
// };
//
// auto move_type_to_string = [](libvgcode::EMoveType type) {
// switch (type) {
// case libvgcode::EMoveType::Noop: { return "EMoveType::Noop"; }
// case libvgcode::EMoveType::Retract: { return "EMoveType::Retract"; }
// case libvgcode::EMoveType::Unretract: { return "EMoveType::Unretract"; }
// case libvgcode::EMoveType::Seam: { return "EMoveType::Seam"; }
// case libvgcode::EMoveType::ToolChange: { return "EMoveType::ToolChange"; }
// case libvgcode::EMoveType::ColorChange: { return "EMoveType::ColorChange"; }
// case libvgcode::EMoveType::PausePrint: { return "EMoveType::PausePrint"; }
// case libvgcode::EMoveType::CustomGCode: { return "EMoveType::CustomGCode"; }
// case libvgcode::EMoveType::Travel: { return "EMoveType::Travel"; }
// case libvgcode::EMoveType::Wipe: { return "EMoveType::Wipe"; }
// case libvgcode::EMoveType::Extrude: { return "EMoveType::Extrude"; }
// case libvgcode::EMoveType::COUNT: { return "EMoveType::COUNT"; }
// }
// };
//
// FilePtr out{ boost::nowide::fopen("C:/prusa/slicer/test_output/spe1872/test.data", "wb") };
// if (out.f != nullptr) {
// const uint32_t vertices_count = static_cast<uint32_t>(data.vertices.size());
// fwrite((void*)&vertices_count, 1, sizeof(uint32_t), out.f);
// for (const libvgcode::PathVertex& v : data.vertices) {
// fwrite((void*)&v.position[0], 1, sizeof(float), out.f);
// fwrite((void*)&v.position[1], 1, sizeof(float), out.f);
// fwrite((void*)&v.position[2], 1, sizeof(float), out.f);
// fwrite((void*)&v.height, 1, sizeof(float), out.f);
// fwrite((void*)&v.width, 1, sizeof(float), out.f);
// fwrite((void*)&v.feedrate, 1, sizeof(float), out.f);
// fwrite((void*)&v.actual_feedrate, 1, sizeof(float), out.f);
// fwrite((void*)&v.mm3_per_mm, 1, sizeof(float), out.f);
// fwrite((void*)&v.fan_speed, 1, sizeof(float), out.f);
// fwrite((void*)&v.temperature, 1, sizeof(float), out.f);
// fwrite((void*)&v.role, 1, sizeof(uint8_t), out.f);
// fwrite((void*)&v.type, 1, sizeof(uint8_t), out.f);
// fwrite((void*)&v.gcode_id, 1, sizeof(uint32_t), out.f);
// fwrite((void*)&v.layer_id, 1, sizeof(uint32_t), out.f);
// fwrite((void*)&v.extruder_id, 1, sizeof(uint32_t), out.f);
// fwrite((void*)&v.color_id, 1, sizeof(uint32_t), out.f);
// fwrite((void*)&v.times[0], 1, sizeof(float), out.f);
// fwrite((void*)&v.times[1], 1, sizeof(float), out.f);
//#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
// const float weight = v.weight;
//#else
// const float weight = 0.0f;
//#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
// fwrite((void*)&weight, 1, sizeof(float), out.f);
// }
//
// const uint8_t spiral_vase_mode = data.spiral_vase_mode ? 1 : 0;
// fwrite((void*)&spiral_vase_mode, 1, sizeof(uint8_t), out.f);
//
// const uint32_t tool_colors_count = static_cast<uint32_t>(data.tools_colors.size());
// fwrite((void*)&tool_colors_count, 1, sizeof(uint32_t), out.f);
// for (const libvgcode::Color& c : data.tools_colors) {
// fwrite((void*)&c[0], 1, sizeof(uint8_t), out.f);
// fwrite((void*)&c[1], 1, sizeof(uint8_t), out.f);
// fwrite((void*)&c[2], 1, sizeof(uint8_t), out.f);
// }
//
// const uint32_t color_print_colors_count = static_cast<uint32_t>(data.color_print_colors.size());
// fwrite((void*)&color_print_colors_count, 1, sizeof(uint32_t), out.f);
// for (const libvgcode::Color& c : data.color_print_colors) {
// fwrite((void*)&c[0], 1, sizeof(uint8_t), out.f);
// fwrite((void*)&c[1], 1, sizeof(uint8_t), out.f);
// fwrite((void*)&c[2], 1, sizeof(uint8_t), out.f);
// }
// }
//#endif // ENABLE_DATA_EXPORT
// send data to the viewer
m_viewer.reset_default_extrusion_roles_colors();
m_viewer.load(std::move(data));
// #if !VGCODE_ENABLE_COG_AND_TOOL_MARKERS
// const size_t vertices_count = m_viewer.get_vertices_count();
// m_cog.reset();
// for (size_t i = 1; i < vertices_count; ++i) {
// const libvgcode::PathVertex& curr = m_viewer.get_vertex_at(i);
// if (curr.type == libvgcode::EMoveType::Extrude &&
// curr.role != libvgcode::EGCodeExtrusionRole::Skirt &&
// curr.role != libvgcode::EGCodeExtrusionRole::SupportMaterial &&
// curr.role != libvgcode::EGCodeExtrusionRole::SupportMaterialInterface &&
// curr.role != libvgcode::EGCodeExtrusionRole::WipeTower &&
// curr.role != libvgcode::EGCodeExtrusionRole::Custom) {
// const Vec3d curr_pos = libvgcode::convert(curr.position).cast<double>();
// const Vec3d prev_pos = libvgcode::convert(m_viewer.get_vertex_at(i - 1).position).cast<double>();
// m_cog.add_segment(curr_pos, prev_pos, gcode_result.filament_densities[curr.extruder_id] * curr.mm3_per_mm * (curr_pos - prev_pos).norm());
// }
// }
// #endif // !VGCODE_ENABLE_COG_AND_TOOL_MARKERS
const libvgcode::AABox bbox = wxGetApp().is_gcode_viewer() ?
m_viewer.get_bounding_box() :
m_viewer.get_extrusion_bounding_box({
libvgcode::EGCodeExtrusionRole::Perimeter, libvgcode::EGCodeExtrusionRole::ExternalPerimeter, libvgcode::EGCodeExtrusionRole::OverhangPerimeter,
libvgcode::EGCodeExtrusionRole::InternalInfill, libvgcode::EGCodeExtrusionRole::SolidInfill, libvgcode::EGCodeExtrusionRole::TopSolidInfill,
libvgcode::EGCodeExtrusionRole::Ironing, libvgcode::EGCodeExtrusionRole::BridgeInfill, libvgcode::EGCodeExtrusionRole::GapFill,
libvgcode::EGCodeExtrusionRole::Skirt, libvgcode::EGCodeExtrusionRole::SupportMaterial, libvgcode::EGCodeExtrusionRole::SupportMaterialInterface,
libvgcode::EGCodeExtrusionRole::WipeTower
});
m_paths_bounding_box = BoundingBoxf3(libvgcode::convert(bbox[0]).cast<double>(), libvgcode::convert(bbox[1]).cast<double>());
if (wxGetApp().is_editor())
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box);
m_extruders_count = gcode_result.extruders_count;
//BBS: move the id to the end of reset
m_last_result_id = gcode_result.id;
m_gcode_result = &gcode_result;
m_only_gcode_in_preview = only_gcode;
m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
//BBS: add only gcode mode
//if (wxGetApp().is_gcode_viewer())
if (m_only_gcode_in_preview)
m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
m_max_print_height = gcode_result.printable_height;
//BBS: add mutex for protection of gcode result
// if (m_layers.empty()) {
// gcode_result.unlock();
// return;
// }
m_settings_ids = gcode_result.settings_ids;
m_filament_diameters = gcode_result.filament_diameters;
m_filament_densities = gcode_result.filament_densities;
m_sequential_view.m_show_marker = false;
//BBS: always load shell at preview
/*if (wxGetApp().is_editor())
{
load_shells(print);
}
else {*/
//BBS: add only gcode mode
if (m_only_gcode_in_preview) {
Pointfs printable_area;
//BBS: add bed exclude area
Pointfs bed_exclude_area = Pointfs();
std::string texture;
std::string model;
if (!gcode_result.printable_area.empty()) {
// bed shape detected in the gcode
printable_area = gcode_result.printable_area;
const auto bundle = wxGetApp().preset_bundle;
if (bundle != nullptr && !m_settings_ids.printer.empty()) {
const Preset* preset = bundle->printers.find_preset(m_settings_ids.printer);
if (preset != nullptr) {
model = PresetUtils::system_printer_bed_model(*preset);
texture = PresetUtils::system_printer_bed_texture(*preset);
}
}
//BBS: add bed exclude area
if (!gcode_result.bed_exclude_area.empty())
bed_exclude_area = gcode_result.bed_exclude_area;
wxGetApp().plater()->set_bed_shape(printable_area, bed_exclude_area, gcode_result.printable_height, texture, model, gcode_result.printable_area.empty());
}
/*else {
// adjust printbed size in dependence of toolpaths bbox
const double margin = 10.0;
const Vec2d min(m_paths_bounding_box.min.x() - margin, m_paths_bounding_box.min.y() - margin);
const Vec2d max(m_paths_bounding_box.max.x() + margin, m_paths_bounding_box.max.y() + margin);
const Vec2d size = max - min;
printable_area = {
{ min.x(), min.y() },
{ max.x(), min.y() },
{ max.x(), min.y() + 0.442265 * size.y()},
{ max.x() - 10.0, min.y() + 0.4711325 * size.y()},
{ max.x() + 10.0, min.y() + 0.5288675 * size.y()},
{ max.x(), min.y() + 0.557735 * size.y()},
{ max.x(), max.y() },
{ min.x() + 0.557735 * size.x(), max.y()},
{ min.x() + 0.5288675 * size.x(), max.y() - 10.0},
{ min.x() + 0.4711325 * size.x(), max.y() + 10.0},
{ min.x() + 0.442265 * size.x(), max.y()},
{ min.x(), max.y() } };
}*/
}
m_print_statistics = gcode_result.print_statistics;
PrintEstimatedStatistics::ETimeMode time_mode = convert(m_viewer.get_time_mode());
if (m_viewer.get_time_mode() != libvgcode::ETimeMode::Normal) {
const float time = m_print_statistics.modes[static_cast<size_t>(time_mode)].time;
if (time == 0.0f ||
short_time(get_time_dhms(time)) == short_time(get_time_dhms(m_print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time)))
m_viewer.set_time_mode(libvgcode::convert(PrintEstimatedStatistics::ETimeMode::Normal));
}
// set to color print by default if use multi extruders
// TODO
// if (m_extruder_ids.size() > 1) {
// for (int i = 0; i < view_type_items.size(); i++) {
// if (view_type_items[i] == libvgcode::EViewType::ColorPrint) {
// m_view_type_sel = i;
// break;
// }
// }
//
// set_view_type(libvgcode::EViewType::ColorPrint);
// }
bool only_gcode_3mf = false;
PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
bool current_has_print_instances = current_plate->has_printable_instances();
if (current_plate->is_slice_result_valid() && wxGetApp().model().objects.empty() && !current_has_print_instances)
only_gcode_3mf = true;
m_layers_slider->set_menu_enable(!(only_gcode || only_gcode_3mf));
m_layers_slider->set_as_dirty();
m_moves_slider->set_as_dirty();
m_conflict_result = gcode_result.conflict_result;
if (m_conflict_result.has_value())
m_conflict_result->layer = m_viewer.get_layer_id_at(static_cast<float>(m_conflict_result->_height));
//BBS: add mutex for protection of gcode result
gcode_result.unlock();
//BBS: add logs
// BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": finished, m_buffers size %1%!")%m_buffers.size();
}
void GCodeViewer::load_as_preview(libvgcode::GCodeInputData&& data)
{
m_loaded_as_preview = true;
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::Skirt, { 127, 255, 127 });
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::ExternalPerimeter, { 255, 255, 0 });
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::SupportMaterial, { 127, 255, 127 });
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::SupportMaterialInterface, { 127, 255, 127 });
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::InternalInfill, { 255, 127, 127 });
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::SolidInfill, { 255, 127, 127 });
m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::WipeTower, { 127, 255, 127 });
m_viewer.load(std::move(data));
const libvgcode::AABox bbox = m_viewer.get_extrusion_bounding_box();
const BoundingBoxf3 paths_bounding_box(libvgcode::convert(bbox[0]).cast<double>(), libvgcode::convert(bbox[1]).cast<double>());
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(GCodeProcessorResult(), paths_bounding_box);
}
void GCodeViewer::update_shells_color_by_extruder(const DynamicPrintConfig *config)
{
if (config != nullptr)
m_shells.volumes.update_colors_by_extruder(config, false);
}
void GCodeViewer::set_shell_transparency(float alpha) { m_shells.volumes.set_transparency(alpha); }
//BBS: always load shell at preview
void GCodeViewer::reset_shell()
{
m_shells.volumes.clear();
m_shells.print_id = -1;
m_shell_bounding_box = BoundingBoxf3();
}
void GCodeViewer::reset()
{
//BBS: should also reset the result id
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": current result id %1% ")%m_last_result_id;
m_last_result_id = -1;
//BBS: add only gcode mode
m_only_gcode_in_preview = false;
m_ssid_to_moveid_map.clear(); // TODO
m_ssid_to_moveid_map.shrink_to_fit(); // TODO
m_viewer.reset();
m_paths_bounding_box = BoundingBoxf3();
m_max_bounding_box = BoundingBoxf3();
m_max_print_height = 0.0f;
m_tools.m_tool_colors = std::vector<ColorRGBA>(); // TODO
m_tools.m_tool_visibles = std::vector<bool>();
m_extruders_count = 0; // TODO
// m_extruder_ids = std::vector<unsigned char>(); // TODO
m_filament_diameters = std::vector<float>();
m_filament_densities = std::vector<float>();
// m_extrusions.reset_ranges(); // TODO
//BBS: always load shell at preview
//m_shells.volumes.clear();
// m_layers.reset(); // TODO
// m_layers_z_range = { 0, 0 }; // TODO
// m_roles = std::vector<ExtrusionRole>();
m_print_statistics.reset();
m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
m_sequential_view.gcode_window.reset();
m_contained_in_bed = true;
}
//BBS: GUI refactor: add canvas width and height
void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin)
{
glsafe(::glEnable(GL_DEPTH_TEST));
render_shells(canvas_width, canvas_height);
if (m_viewer.get_extrusion_roles().empty())
return;
render_toolpaths(); // TODO: m_viewer.get_layers_count() > 0
float legend_height = 0.0f;
render_legend(legend_height, canvas_width, canvas_height, right_margin);
if (m_user_mode != wxGetApp().get_mode()) {
update_by_mode(wxGetApp().get_mode());
m_user_mode = wxGetApp().get_mode();
}
//BBS fixed bottom_margin for space to render horiz slider
int bottom_margin = SLIDER_BOTTOM_MARGIN * GCODE_VIEWER_SLIDER_SCALE;
auto current = m_viewer.get_view_visible_range(); // TODO: verify
auto endpoints = m_viewer.get_view_full_range();
m_sequential_view.m_show_marker = m_sequential_view.m_show_marker || (current.back() != endpoints.back() && !m_no_render_path);
// BBS fixed buttom margin. m_moves_slider.pos_y
m_sequential_view.render(!m_no_render_path, legend_height, &m_viewer, m_viewer.get_current_vertex().gcode_id, canvas_width, canvas_height - bottom_margin * m_scale, right_margin * m_scale, m_viewer.get_view_type());
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
if (is_legend_shown()) {
ImGuiWrapper& imgui = *Slic3r::GUI::wxGetApp().imgui();
const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
ImGuiPureWrap::set_next_window_pos(static_cast<float>(cnv_size.get_width()), static_cast<float>(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f);
ImGuiPureWrap::begin(std::string("LibVGCode Viewer Controller"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
ImGuiPureWrap::checkbox("Cog marker fixed screen size", m_cog_marker_fixed_screen_size);
if (ImGui::BeginTable("Cog", 2)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Cog marker size");
ImGui::TableSetColumnIndex(1);
imgui.slider_float("##CogSize", &m_cog_marker_size, 1.0f, 5.0f);
ImGui::EndTable();
}
ImGuiPureWrap::checkbox("Tool marker fixed screen size", m_tool_marker_fixed_screen_size);
if (ImGui::BeginTable("Tool", 2)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Tool marker size");
ImGui::TableSetColumnIndex(1);
imgui.slider_float("##ToolSize", &m_tool_marker_size, 1.0f, 5.0f);
ImGui::EndTable();
}
ImGuiPureWrap::end();
}
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
//BBS render slider
render_slider(canvas_width, canvas_height);
}
#define ENABLE_CALIBRATION_THUMBNAIL_OUTPUT 0
#if ENABLE_CALIBRATION_THUMBNAIL_OUTPUT
static void debug_calibration_output_thumbnail(const ThumbnailData& thumbnail_data)
{
// debug export of generated image
wxImage image(thumbnail_data.width, thumbnail_data.height);
image.InitAlpha();
for (unsigned int r = 0; r < thumbnail_data.height; ++r)
{
unsigned int rr = (thumbnail_data.height - 1 - r) * thumbnail_data.width;
for (unsigned int c = 0; c < thumbnail_data.width; ++c)
{
unsigned char* px = (unsigned char*)thumbnail_data.pixels.data() + 4 * (rr + c);
image.SetRGB((int)c, (int)r, px[0], px[1], px[2]);
image.SetAlpha((int)c, (int)r, px[3]);
}
}
image.SaveFile("D:/calibrate.png", wxBITMAP_TYPE_PNG);
}
#endif
// void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager)
// {
// int plate_idx = thumbnail_params.plate_id;
// PartPlate* plate = partplate_list.get_plate(plate_idx);
// BoundingBoxf3 plate_box = plate->get_bounding_box(false);
// plate_box.min.z() = 0.0;
// plate_box.max.z() = 0.0;
// Vec3d center = plate_box.center();
//
// #if 1
// Camera camera;
// camera.set_viewport(0, 0, thumbnail_data.width, thumbnail_data.height);
// camera.apply_viewport();
// camera.set_scene_box(plate_box);
// camera.set_type(Camera::EType::Ortho);
// camera.set_target(center);
// camera.select_view("top");
// camera.zoom_to_box(plate_box, 1.0f);
// camera.apply_projection(plate_box);
//
// auto render_as_triangles = [
// #if ENABLE_GCODE_VIEWER_STATISTICS
// this
// #endif // ENABLE_GCODE_VIEWER_STATISTICS
// ](TBuffer &buffer, std::vector<RenderPath>::iterator it_path, std::vector<RenderPath>::iterator it_end, GLShaderProgram& shader, int uniform_color) {
// for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) {
// const RenderPath& path = *it;
// // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415.
// assert(!path.sizes.empty());
// assert(!path.offsets.empty());
// shader.set_uniform(uniform_color, path.color);
// glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
// #if ENABLE_GCODE_VIEWER_STATISTICS
// ++m_statistics.gl_multi_triangles_calls_count;
// #endif // ENABLE_GCODE_VIEWER_STATISTICS
// }
// };
//
// auto render_as_instanced_model = [
// #if ENABLE_GCODE_VIEWER_STATISTICS
// this
// #endif // ENABLE_GCODE_VIEWER_STATISTICS
// ](TBuffer& buffer, GLShaderProgram& shader) {
// for (auto& range : buffer.model.instances.render_ranges.ranges) {
// if (range.vbo == 0 && range.count > 0) {
// glsafe(::glGenBuffers(1, &range.vbo));
// glsafe(::glBindBuffer(GL_ARRAY_BUFFER, range.vbo));
// glsafe(::glBufferData(GL_ARRAY_BUFFER, range.count * buffer.model.instances.instance_size_bytes(), (const void*)&buffer.model.instances.buffer[range.offset * buffer.model.instances.instance_size_floats()], GL_STATIC_DRAW));
// glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
// }
//
// if (range.vbo > 0) {
// buffer.model.model.set_color(range.color);
// buffer.model.model.render_instanced(range.vbo, range.count);
// #if ENABLE_GCODE_VIEWER_STATISTICS
// ++m_statistics.gl_instanced_models_calls_count;
// m_statistics.total_instances_gpu_size += static_cast<int64_t>(range.count * buffer.model.instances.instance_size_bytes());
// #endif // ENABLE_GCODE_VIEWER_STATISTICS
// }
// }
// };
//
// #if ENABLE_GCODE_VIEWER_STATISTICS
// auto render_as_batched_model = [this](TBuffer& buffer, GLShaderProgram& shader) {
// #else
// auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader, int position_id, int normal_id) {
// #endif // ENABLE_GCODE_VIEWER_STATISTICS
//
// struct Range
// {
// unsigned int first;
// unsigned int last;
// bool intersects(const Range& other) const { return (other.last < first || other.first > last) ? false : true; }
// };
// Range buffer_range = { 0, 0 };
// size_t indices_per_instance = buffer.model.data.indices_count();
//
// for (size_t j = 0; j < buffer.indices.size(); ++j) {
// const IBuffer& i_buffer = buffer.indices[j];
// buffer_range.last = buffer_range.first + i_buffer.count / indices_per_instance;
// glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
// if (position_id != -1) {
// glsafe(::glVertexAttribPointer(position_id, buffer.vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes()));
// glsafe(::glEnableVertexAttribArray(position_id));
// }
// bool has_normals = buffer.vertices.normal_size_floats() > 0;
// if (has_normals) {
// if (normal_id != -1) {
// glsafe(::glVertexAttribPointer(normal_id, buffer.vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes()));
// glsafe(::glEnableVertexAttribArray(normal_id));
// }
// }
//
// glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
//
// for (auto& range : buffer.model.instances.render_ranges.ranges) {
// Range range_range = { range.offset, range.offset + range.count };
// if (range_range.intersects(buffer_range)) {
// shader.set_uniform("uniform_color", range.color);
// unsigned int offset = (range_range.first > buffer_range.first) ? range_range.first - buffer_range.first : 0;
// size_t offset_bytes = static_cast<size_t>(offset) * indices_per_instance * sizeof(IBufferType);
// Range render_range = { std::max(range_range.first, buffer_range.first), std::min(range_range.last, buffer_range.last) };
// size_t count = static_cast<size_t>(render_range.last - render_range.first) * indices_per_instance;
// if (count > 0) {
// glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)count, GL_UNSIGNED_SHORT, (const void*)offset_bytes));
// #if ENABLE_GCODE_VIEWER_STATISTICS
// ++m_statistics.gl_batched_models_calls_count;
// #endif // ENABLE_GCODE_VIEWER_STATISTICS
// }
// }
// }
//
// glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
//
// if (normal_id != -1)
// glsafe(::glDisableVertexAttribArray(normal_id));
// if (position_id != -1)
// glsafe(::glDisableVertexAttribArray(position_id));
// glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
//
// buffer_range.first = buffer_range.last;
// }
// };
//
// unsigned char begin_id = buffer_id(EMoveType::Retract);
// unsigned char end_id = buffer_id(EMoveType::Count);
//
// BOOST_LOG_TRIVIAL(info) << boost::format("render_calibration_thumbnail: begin_id %1%, end_id %2%")%begin_id %end_id;
// for (unsigned char i = begin_id; i < end_id; ++i) {
// TBuffer& buffer = m_buffers[i];
// if (!buffer.visible || !buffer.has_data())
// continue;
//
// GLShaderProgram* shader = opengl_manager.get_shader("flat");
// if (shader != nullptr) {
// shader->start_using();
//
// shader->set_uniform("view_model_matrix", camera.get_view_matrix());
// shader->set_uniform("projection_matrix", camera.get_projection_matrix());
// int position_id = shader->get_attrib_location("v_position");
// int normal_id = shader->get_attrib_location("v_normal");
//
// if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) {
// //shader->set_uniform("emission_factor", 0.25f);
// render_as_instanced_model(buffer, *shader);
// //shader->set_uniform("emission_factor", 0.0f);
// }
// else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) {
// //shader->set_uniform("emission_factor", 0.25f);
// render_as_batched_model(buffer, *shader, position_id, normal_id);
// //shader->set_uniform("emission_factor", 0.0f);
// }
// else {
// int uniform_color = shader->get_uniform_location("uniform_color");
// auto it_path = buffer.render_paths.begin();
// for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast<unsigned int>(buffer.indices.size()); ++ibuffer_id) {
// const IBuffer& i_buffer = buffer.indices[ibuffer_id];
// // Skip all paths with ibuffer_id < ibuffer_id.
// for (; it_path != buffer.render_paths.end() && it_path->ibuffer_id < ibuffer_id; ++it_path);
// if (it_path == buffer.render_paths.end() || it_path->ibuffer_id > ibuffer_id)
// // Not found. This shall not happen.
// continue;
//
// glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
// if (position_id != -1) {
// glsafe(::glVertexAttribPointer(position_id, buffer.vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes()));
// glsafe(::glEnableVertexAttribArray(position_id));
// }
// bool has_normals = false;// buffer.vertices.normal_size_floats() > 0;
// if (has_normals) {
// if (normal_id != -1) {
// glsafe(::glVertexAttribPointer(normal_id, buffer.vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes()));
// glsafe(::glEnableVertexAttribArray(normal_id));
// }
// }
//
// glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
//
// // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors.
// switch (buffer.render_primitive_type)
// {
// case TBuffer::ERenderPrimitiveType::Triangle: {
// render_as_triangles(buffer, it_path, buffer.render_paths.end(), *shader, uniform_color);
// break;
// }
// default: { break; }
// }
//
// glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
//
// if (normal_id != -1)
// glsafe(::glDisableVertexAttribArray(normal_id));
// if (position_id != -1)
// glsafe(::glDisableVertexAttribArray(position_id));
//
// glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
// }
// }
//
// shader->stop_using();
// }
// else {
// BOOST_LOG_TRIVIAL(info) << boost::format("render_calibration_thumbnail: can not find shader");
// }
// }
// #endif
// BOOST_LOG_TRIVIAL(info) << boost::format("render_calibration_thumbnail: exit");
//
// }
//
// void GCodeViewer::_render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager)
// {
// BOOST_LOG_TRIVIAL(info) << boost::format("render_calibration_thumbnail prepare: width %1%, height %2%")%w %h;
// thumbnail_data.set(w, h);
// if (!thumbnail_data.is_valid())
// return;
//
// //TODO bool multisample = m_multisample_allowed;
// bool multisample = OpenGLManager::can_multisample();
// //if (!multisample)
// // glsafe(::glEnable(GL_MULTISAMPLE));
//
// GLint max_samples;
// glsafe(::glGetIntegerv(GL_MAX_SAMPLES, &max_samples));
// GLsizei num_samples = max_samples / 2;
//
// GLuint render_fbo;
// glsafe(::glGenFramebuffers(1, &render_fbo));
// glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo));
// BOOST_LOG_TRIVIAL(info) << boost::format("render_calibration_thumbnail prepare: max_samples %1%, multisample %2%, render_fbo %3%")%max_samples %multisample %render_fbo;
//
// GLuint render_tex = 0;
// GLuint render_tex_buffer = 0;
// if (multisample) {
// // use renderbuffer instead of texture to avoid the need to use glTexImage2DMultisample which is available only since OpenGL 3.2
// glsafe(::glGenRenderbuffers(1, &render_tex_buffer));
// glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_tex_buffer));
// glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_RGBA8, w, h));
// glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_tex_buffer));
// }
// else {
// glsafe(::glGenTextures(1, &render_tex));
// glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex));
// glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
// glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
// glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
// glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0));
// }
//
// GLuint render_depth;
// glsafe(::glGenRenderbuffers(1, &render_depth));
// glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth));
// if (multisample)
// glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_DEPTH_COMPONENT24, w, h));
// else
// glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h));
//
// glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth));
//
// GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 };
// glsafe(::glDrawBuffers(1, drawBufs));
//
//
// if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
// _render_calibration_thumbnail_internal(thumbnail_data, thumbnail_params, partplate_list, opengl_manager);
//
// if (multisample) {
// GLuint resolve_fbo;
// glsafe(::glGenFramebuffers(1, &resolve_fbo));
// glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, resolve_fbo));
//
// GLuint resolve_tex;
// glsafe(::glGenTextures(1, &resolve_tex));
// glsafe(::glBindTexture(GL_TEXTURE_2D, resolve_tex));
// glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
// glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
// glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
// glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolve_tex, 0));
//
// glsafe(::glDrawBuffers(1, drawBufs));
//
// if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
// glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, render_fbo));
// glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo));
// glsafe(::glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR));
//
// glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo));
// glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
// }
//
// glsafe(::glDeleteTextures(1, &resolve_tex));
// glsafe(::glDeleteFramebuffers(1, &resolve_fbo));
// }
// else
// glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
// }
// #if ENABLE_CALIBRATION_THUMBNAIL_OUTPUT
// debug_calibration_output_thumbnail(thumbnail_data);
// #endif
//
// glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
// glsafe(::glDeleteRenderbuffers(1, &render_depth));
// if (render_tex_buffer != 0)
// glsafe(::glDeleteRenderbuffers(1, &render_tex_buffer));
// if (render_tex != 0)
// glsafe(::glDeleteTextures(1, &render_tex));
// glsafe(::glDeleteFramebuffers(1, &render_fbo));
//
// //if (!multisample)
// // glsafe(::glDisable(GL_MULTISAMPLE));
// BOOST_LOG_TRIVIAL(info) << boost::format("render_calibration_thumbnail prepare: exit");
// }
//BBS
// void GCodeViewer::render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager)
// {
// // reset values and refresh render
// int last_view_type_sel = m_view_type_sel;
// EViewType last_view_type = m_view_type;
// unsigned int last_role_visibility_flags = m_extrusions.role_visibility_flags;
// // set color scheme to FilamentId
// for (int i = 0; i < view_type_items.size(); i++) {
// if (view_type_items[i] == EViewType::FilamentId) {
// m_view_type_sel = i;
// break;
// }
// }
// set_view_type(EViewType::FilamentId, false);
// // set m_layers_z_range to 0, 1;
// // To be safe, we include both layers here although layer 1 seems enough
// // layer 0: custom extrusions such as flow calibration etc.
// // layer 1: the real first layer of object
// std::array<unsigned int, 2> tmp_layers_z_range = m_layers_z_range;
// m_layers_z_range = {0, 1};
// // BBS exclude feature types
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags & ~(1 << erSkirt);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags & ~(1 << erCustom);
// // BBS include feature types
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erWipeTower);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erPerimeter);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erExternalPerimeter);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erOverhangPerimeter);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erSolidInfill);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erTopSolidInfill);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erInternalInfill);
// m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erBottomSurface);
//
// refresh_render_paths(false, false);
//
// _render_calibration_thumbnail_framebuffer(thumbnail_data, w, h, thumbnail_params, partplate_list, opengl_manager);
//
// // restore values and refresh render
// // reset m_layers_z_range and view type
// m_view_type_sel = last_view_type_sel;
// set_view_type(last_view_type, false);
// m_layers_z_range = tmp_layers_z_range;
// m_extrusions.role_visibility_flags = last_role_visibility_flags;
// refresh_render_paths(false, false);
// }
bool GCodeViewer::can_export_toolpaths() const
{
const libvgcode::Interval& visible_range = m_viewer.get_view_visible_range();
for (size_t i = visible_range[0]; i <= visible_range[1]; ++i) {
if (m_viewer.get_vertex_at(i).is_extrusion())
return true;
}
return false;
}
void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned int last)
{
m_viewer.set_view_visible_range(static_cast<uint32_t>(first), static_cast<uint32_t>(last));
const libvgcode::Interval& enabled_range = m_viewer.get_view_enabled_range();
enable_moves_slider(enabled_range[1] > enabled_range[0]);
#if ENABLE_ACTUAL_SPEED_DEBUG
if (enabled_range[1] != m_viewer.get_view_visible_range()[1]) {
const libvgcode::PathVertex& curr_vertex = m_viewer.get_current_vertex();
if (curr_vertex.is_extrusion() || curr_vertex.is_travel() || curr_vertex.is_wipe() ||
curr_vertex.type == libvgcode::EMoveType::Seam) {
const libvgcode::ColorRange& color_range = m_viewer.get_color_range(libvgcode::EViewType::ActualSpeed);
const std::array<float, 2>& interval = color_range.get_range();
const size_t vertices_count = m_viewer.get_vertices_count();
std::vector<SequentialView::ActualSpeedImguiWidget::Item> actual_speed_data;
// collect vertices sharing the same gcode_id
const size_t curr_id = m_viewer.get_current_vertex_id();
size_t start_id = curr_id;
while (start_id > 0) {
--start_id;
if (curr_vertex.gcode_id != m_viewer.get_vertex_at(start_id).gcode_id)
break;
}
size_t end_id = curr_id;
while (end_id < vertices_count - 1) {
++end_id;
if (curr_vertex.gcode_id != m_viewer.get_vertex_at(end_id).gcode_id)
break;
}
if (m_viewer.get_vertex_at(end_id - 1).type == libvgcode::convert(EMoveType::Seam))
--end_id;
assert(end_id - start_id >= 2);
float total_len = 0.0f;
for (size_t i = start_id; i < end_id; ++i) {
const libvgcode::PathVertex& v = m_viewer.get_vertex_at(i);
const float len = (i > start_id) ?
(libvgcode::convert(v.position) - libvgcode::convert(m_viewer.get_vertex_at(i - 1).position)).norm() : 0.0f;
total_len += len;
if (i == start_id || len > EPSILON)
actual_speed_data.push_back({ total_len, v.actual_feedrate, v.times[0] == 0.0f });
}
std::vector<std::pair<float, ColorRGBA>> levels;
const std::vector<float> values = color_range.get_values();
for (float value : values) {
levels.push_back(std::make_pair(value, libvgcode::convert(color_range.get_color_at(value))));
levels.back().second.a(0.5f);
}
m_sequential_view.marker.set_actual_speed_data(actual_speed_data);
m_sequential_view.marker.set_actual_speed_y_range(std::make_pair(interval[0], interval[1]));
m_sequential_view.marker.set_actual_speed_levels(levels);
}
}
#endif // ENABLE_ACTUAL_SPEED_DEBUG
}
void GCodeViewer::enable_moves_slider(bool enable) const
{
bool render_as_disabled = !enable;
if (m_moves_slider != nullptr && m_moves_slider->is_rendering_as_disabled() != render_as_disabled) {
m_moves_slider->set_render_as_disabled(render_as_disabled);
m_moves_slider->set_as_dirty();
}
}
void GCodeViewer::update_moves_slider(bool set_to_max)
{
if (m_gcode_result->moves.empty())
return;
const libvgcode::Interval& range = m_viewer.get_view_enabled_range();
uint32_t last_gcode_id = get_gcode_vertex_at(range[0]).gcode_id;
// std::optional<uint32_t> gcode_id_min = visible_range_min.has_value() ?
// std::optional<uint32_t>{ get_gcode_vertex_at(*visible_range_min).gcode_id } : std::nullopt;
// std::optional<uint32_t> gcode_id_max = visible_range_max.has_value() ?
// std::optional<uint32_t>{ get_gcode_vertex_at(*visible_range_max).gcode_id } : std::nullopt;
const size_t range_size = range[1] - range[0] + 1;
std::vector<double> values;
values.reserve(range_size);
std::vector<double> alternate_values;
alternate_values.reserve(range_size);
std::optional<uint32_t> visible_range_min_id;
std::optional<uint32_t> visible_range_max_id;
uint32_t counter = 0;
for (size_t i = range[0]; i <= range[1]; ++i) {
const uint32_t gcode_id = get_gcode_vertex_at(i).gcode_id;
bool skip = false;
if (i > range[0]) {
// skip consecutive moves with same gcode id (resulting from processing G2 and G3 lines)
if (last_gcode_id == gcode_id) {
values.back() = i + 1;
skip = true;
}
else
last_gcode_id = gcode_id;
}
if (!skip) {
values.emplace_back(i + 1);
alternate_values.emplace_back(gcode_id);
// if (gcode_id_min.has_value() && alternate_values.back() == *gcode_id_min)
// visible_range_min_id = counter;
// else if (gcode_id_max.has_value() && alternate_values.back() == *gcode_id_max)
// visible_range_max_id = counter;
++counter;
}
}
const int span_min_id = visible_range_min_id.has_value() ? *visible_range_min_id : 0;
const int span_max_id = visible_range_max_id.has_value() ? *visible_range_max_id : static_cast<int>(values.size()) - 1;
// const GCodeViewer::SequentialView &view = get_sequential_view();
// // this should not be needed, but it is here to try to prevent rambling crashes on Mac Asan
// if (view.endpoints.last < view.endpoints.first) return;
//
// std::vector<double> values(view.endpoints.last - view.endpoints.first + 1);
// std::vector<double> alternate_values(view.endpoints.last - view.endpoints.first + 1);
// unsigned int count = 0;
// for (unsigned int i = view.endpoints.first; i <= view.endpoints.last; ++i) {
// values[count] = static_cast<double>(i + 1);
// if (view.gcode_ids[i] > 0) alternate_values[count] = static_cast<double>(view.gcode_ids[i]);
// ++count;
// }
bool keep_min = m_moves_slider->GetActiveValue() == m_moves_slider->GetMinValue();
m_moves_slider->SetSliderValues(values);
m_moves_slider->SetSliderAlternateValues(alternate_values);
m_moves_slider->SetMaxValue(static_cast<int>(values.size()) - 1);
m_moves_slider->SetSelectionSpan(span_min_id, span_max_id);
if (set_to_max)
m_moves_slider->SetHigherValue(keep_min ? m_moves_slider->GetMinValue() : m_moves_slider->GetMaxValue());
}
void GCodeViewer::update_layers_slider_mode()
{
// true -> single-extruder printer profile OR
// multi-extruder printer profile , but whole model is printed by only one extruder
// false -> multi-extruder printer profile , and model is printed by several extruders
bool one_extruder_printed_model = true;
// extruder used for whole model for multi-extruder printer profile
int only_extruder = -1;
// BBS
if (wxGetApp().filaments_cnt() > 1) {
const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects;
// check if whole model uses just only one extruder
if (!objects.empty()) {
const int extruder = objects[0]->config.has("extruder") ? objects[0]->config.option("extruder")->getInt() : 0;
auto is_one_extruder_printed_model = [objects, extruder]() {
for (ModelObject *object : objects) {
if (object->config.has("extruder") && object->config.option("extruder")->getInt() != extruder) return false;
for (ModelVolume *volume : object->volumes)
if ((volume->config.has("extruder") && volume->config.option("extruder")->getInt() != extruder) || !volume->mmu_segmentation_facets.empty()) return false;
for (const auto &range : object->layer_config_ranges)
if (range.second.has("extruder") && range.second.option("extruder")->getInt() != extruder) return false;
}
return true;
};
if (is_one_extruder_printed_model())
only_extruder = extruder;
else
one_extruder_printed_model = false;
}
}
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
}
void GCodeViewer::update_marker_curr_move() {
if ((int)m_last_result_id != -1) {
auto it = std::find_if(m_gcode_result->moves.begin(), m_gcode_result->moves.end(), [this](auto move) {
auto range = m_viewer.get_view_enabled_range();
auto full_range = m_viewer.get_view_full_range();
if (range.back() < full_range.back() && range.back() >= 0) {
return move.gcode_id == static_cast<uint64_t>(m_viewer.get_vertex_at(range.back()).gcode_id);
}
return false;
});
if (it != m_gcode_result->moves.end())
m_sequential_view.marker.update_curr_move(*it);
}
}
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
{
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
update_moves_slider(true);
}
class ToolpathsObjExporter
{
public:
explicit ToolpathsObjExporter(const libvgcode::Viewer& viewer)
: m_viewer(viewer) {
}
void export_to(const std::string& filename) {
CNumericLocalesSetter locales_setter;
// open geometry file
FilePtr f_geo = boost::nowide::fopen(filename.c_str(), "w");
if (f_geo.f == nullptr) {
BOOST_LOG_TRIVIAL(error) << "ToolpathsObjExporter: Couldn't open " << filename << " for writing";
return;
}
boost::filesystem::path materials_filename(filename);
materials_filename.replace_extension("mtl");
// write header to geometry file
fprintf(f_geo.f, "# G-Code Toolpaths\n");
fprintf(f_geo.f, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SoftFever_VERSION);
fprintf(f_geo.f, "\nmtllib ./%s\n", materials_filename.filename().string().c_str());
// open material file
FilePtr f_mat = boost::nowide::fopen(materials_filename.string().c_str(), "w");
if (f_mat.f == nullptr) {
BOOST_LOG_TRIVIAL(error) << "ToolpathsObjExporter: Couldn't open " << materials_filename.string() << " for writing";
return;
}
// write header to material file
fprintf(f_mat.f, "# G-Code Toolpaths Materials\n");
fprintf(f_mat.f, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SoftFever_VERSION);
libvgcode::Interval visible_range = m_viewer.get_view_visible_range();
if (m_viewer.is_top_layer_only_view_range())
visible_range[0] = m_viewer.get_view_full_range()[0];
for (size_t i = visible_range[0]; i <= visible_range[1]; ++i) {
const libvgcode::PathVertex& curr = m_viewer.get_vertex_at(i);
const libvgcode::PathVertex& next = m_viewer.get_vertex_at(i + 1);
if (!curr.is_extrusion() || !next.is_extrusion())
continue;
const libvgcode::PathVertex& nextnext = m_viewer.get_vertex_at(i + 2);
unsigned char flags = 0;
if (curr.gcode_id == next.gcode_id)
flags |= Flag_First;
if (i + 1 == visible_range[1] || !nextnext.is_extrusion())
flags |= Flag_Last;
else
flags |= Flag_Internal;
export_segment(*f_geo.f, flags, i, curr, next, nextnext);
}
export_materials(*f_mat.f);
}
private:
const libvgcode::Viewer& m_viewer;
size_t m_vertices_count{ 0 };
std::vector<libvgcode::Color> m_colors;
static const unsigned char Flag_First = 0x01;
static const unsigned char Flag_Last = 0x02;
static const unsigned char Flag_Internal = 0x04;
static const float Cap_Rounding_Factor;
struct SegmentLocalAxes
{
Vec3f forward;
Vec3f right;
Vec3f up;
};
SegmentLocalAxes segment_local_axes(const Vec3f& v1, const Vec3f& v2) {
SegmentLocalAxes ret;
ret.forward = (v2 - v1).normalized();
ret.right = ret.forward.cross(Vec3f::UnitZ()).normalized();
ret.up = ret.right.cross(ret.forward);
return ret;
}
struct Vertex
{
Vec3f position;
Vec3f normal;
};
struct CrossSection
{
Vertex right;
Vertex top;
Vertex left;
Vertex bottom;
};
CrossSection cross_section(const Vec3f& v, const Vec3f& right, const Vec3f& up, float width, float height) {
CrossSection ret;
const Vec3f w_shift = 0.5f * width * right;
const Vec3f h_shift = 0.5f * height * up;
ret.right.position = v + w_shift;
ret.right.normal = right;
ret.top.position = v + h_shift;
ret.top.normal = up;
ret.left.position = v - w_shift;
ret.left.normal = -right;
ret.bottom.position = v - h_shift;
ret.bottom.normal = -up;
return ret;
}
CrossSection normal_cross_section(const Vec3f& v, const SegmentLocalAxes& axes, float width, float height) {
return cross_section(v, axes.right, axes.up, width, height);
}
enum CornerType : unsigned char
{
RightTurn,
LeftTurn,
Straight
};
CrossSection corner_cross_section(const Vec3f& v, const SegmentLocalAxes& axes1, const SegmentLocalAxes& axes2,
float width, float height, CornerType& corner_type) {
if (std::abs(std::abs(axes1.forward.dot(axes2.forward)) - 1.0f) < EPSILON)
corner_type = CornerType::Straight;
else if (axes1.up.dot(axes1.forward.cross(axes2.forward)) < 0.0f)
corner_type = CornerType::RightTurn;
else
corner_type = CornerType::LeftTurn;
const Vec3f right = (0.5f * (axes1.right + axes2.right)).normalized();
return cross_section(v, right, axes1.up, width, height);
}
void export_segment(FILE& f, unsigned char flags, size_t v1_id, const libvgcode::PathVertex& v1, const libvgcode::PathVertex& v2, const libvgcode::PathVertex& v3) {
const Vec3f v1_pos = libvgcode::convert(v1.position);
const Vec3f v2_pos = libvgcode::convert(v2.position);
const Vec3f v3_pos = libvgcode::convert(v3.position);
const SegmentLocalAxes v1_v2 = segment_local_axes(v1_pos, v2_pos);
const SegmentLocalAxes v2_v3 = segment_local_axes(v2_pos, v3_pos);
// starting cap
if ((flags & Flag_First) > 0) {
const Vertex v0 = { v1_pos - Cap_Rounding_Factor * v1.width * v1_v2.forward, -v1_v2.forward };
const CrossSection ncs = normal_cross_section(v1_pos, v1_v2, v1.width, v1.height);
export_vertex(f, v0); // 0
export_vertex(f, ncs.right); // 1
export_vertex(f, ncs.top); // 2
export_vertex(f, ncs.left); // 3
export_vertex(f, ncs.bottom); // 4
export_material(f, color_id(v1_id));
export_triangle(f, vertex_id(0), vertex_id(1), vertex_id(2));
export_triangle(f, vertex_id(0), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(0), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(0), vertex_id(4), vertex_id(1));
m_vertices_count += 5;
}
// segment body + ending cap
if ((flags & Flag_Last) > 0) {
const Vertex v0 = { v2_pos + Cap_Rounding_Factor * v2.width * v1_v2.forward, v1_v2.forward };
const CrossSection ncs = normal_cross_section(v2_pos, v1_v2, v2.width, v2.height);
export_vertex(f, v0); // 0
export_vertex(f, ncs.right); // 1
export_vertex(f, ncs.top); // 2
export_vertex(f, ncs.left); // 3
export_vertex(f, ncs.bottom); // 4
export_material(f, color_id(v1_id + 1));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(1), vertex_id(2));
export_triangle(f, vertex_id(-4), vertex_id(2), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(-3), vertex_id(3), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(-2), vertex_id(4), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(4), vertex_id(1));
export_triangle(f, vertex_id(-1), vertex_id(1), vertex_id(-4));
// ending cap
export_triangle(f, vertex_id(0), vertex_id(3), vertex_id(2));
export_triangle(f, vertex_id(0), vertex_id(2), vertex_id(1));
export_triangle(f, vertex_id(0), vertex_id(1), vertex_id(4));
export_triangle(f, vertex_id(0), vertex_id(4), vertex_id(3));
m_vertices_count += 5;
}
else {
CornerType corner_type;
const CrossSection ccs = corner_cross_section(v2_pos, v1_v2, v2_v3, v2.width, v2.height, corner_type);
const CrossSection ncs12 = normal_cross_section(v2_pos, v1_v2, v2.width, v2.height);
const CrossSection ncs23 = normal_cross_section(v2_pos, v2_v3, v2.width, v2.height);
if (corner_type == CornerType::Straight) {
export_vertex(f, ncs12.right); // 0
export_vertex(f, ncs12.top); // 1
export_vertex(f, ncs12.left); // 2
export_vertex(f, ncs12.bottom); // 3
export_material(f, color_id(v1_id + 1));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(0), vertex_id(1));
export_triangle(f, vertex_id(-4), vertex_id(1), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(1), vertex_id(2));
export_triangle(f, vertex_id(-3), vertex_id(2), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(-2), vertex_id(3), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(3), vertex_id(0));
export_triangle(f, vertex_id(-1), vertex_id(0), vertex_id(-4));
m_vertices_count += 4;
}
else if (corner_type == CornerType::RightTurn) {
export_vertex(f, ncs12.left); // 0
export_vertex(f, ccs.left); // 1
export_vertex(f, ccs.right); // 2
export_vertex(f, ncs12.top); // 3
export_vertex(f, ncs23.left); // 4
export_vertex(f, ncs12.bottom); // 5
export_material(f, color_id(v1_id + 1));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(-4), vertex_id(3), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(3), vertex_id(0));
export_triangle(f, vertex_id(-3), vertex_id(0), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(0), vertex_id(5));
export_triangle(f, vertex_id(-2), vertex_id(5), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(5), vertex_id(2));
export_triangle(f, vertex_id(-1), vertex_id(2), vertex_id(-4));
// corner
export_triangle(f, vertex_id(1), vertex_id(0), vertex_id(3));
export_triangle(f, vertex_id(1), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(1), vertex_id(4), vertex_id(5));
export_triangle(f, vertex_id(1), vertex_id(5), vertex_id(0));
m_vertices_count += 6;
}
else {
export_vertex(f, ncs12.right); // 0
export_vertex(f, ccs.right); // 1
export_vertex(f, ncs23.right); // 2
export_vertex(f, ncs12.top); // 3
export_vertex(f, ccs.left); // 4
export_vertex(f, ncs12.bottom); // 5
export_material(f, color_id(v1_id + 1));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(0), vertex_id(3));
export_triangle(f, vertex_id(-4), vertex_id(3), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(-3), vertex_id(4), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(4), vertex_id(5));
export_triangle(f, vertex_id(-2), vertex_id(5), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(5), vertex_id(0));
export_triangle(f, vertex_id(-1), vertex_id(0), vertex_id(-4));
// corner
export_triangle(f, vertex_id(1), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(1), vertex_id(3), vertex_id(0));
export_triangle(f, vertex_id(1), vertex_id(0), vertex_id(5));
export_triangle(f, vertex_id(1), vertex_id(5), vertex_id(2));
m_vertices_count += 6;
}
}
}
size_t vertex_id(int id) { return static_cast<size_t>(1 + static_cast<int>(m_vertices_count) + id); }
void export_vertex(FILE& f, const Vertex& v) {
fprintf(&f, "v %g %g %g\n", v.position.x(), v.position.y(), v.position.z());
fprintf(&f, "vn %g %g %g\n", v.normal.x(), v.normal.y(), v.normal.z());
}
void export_material(FILE& f, size_t material_id) {
fprintf(&f, "\nusemtl material_%zu\n", material_id + 1);
}
void export_triangle(FILE& f, size_t v1, size_t v2, size_t v3) {
fprintf(&f, "f %zu//%zu %zu//%zu %zu//%zu\n", v1, v1, v2, v2, v3, v3);
}
void export_materials(FILE& f) {
static const float inv_255 = 1.0f / 255.0f;
size_t materials_counter = 0;
for (const auto& color : m_colors) {
fprintf(&f, "\nnewmtl material_%zu\n", ++materials_counter);
fprintf(&f, "Ka 1 1 1\n");
fprintf(&f, "Kd %g %g %g\n", static_cast<float>(color[0]) * inv_255,
static_cast<float>(color[1]) * inv_255,
static_cast<float>(color[2]) * inv_255);
fprintf(&f, "Ks 0 0 0\n");
}
}
size_t color_id(size_t vertex_id) {
const libvgcode::PathVertex& v = m_viewer.get_vertex_at(vertex_id);
const size_t top_layer_id = m_viewer.is_top_layer_only_view_range() ? m_viewer.get_layers_view_range()[1] : 0;
const bool color_top_layer_only = m_viewer.get_view_full_range()[1] != m_viewer.get_view_visible_range()[1];
const libvgcode::Color color = (color_top_layer_only && v.layer_id < top_layer_id &&
(!m_viewer.is_spiral_vase_mode() || vertex_id != m_viewer.get_view_enabled_range()[0])) ?
libvgcode::DUMMY_COLOR : m_viewer.get_vertex_color(v);
auto color_it = std::find_if(m_colors.begin(), m_colors.end(), [&color](const libvgcode::Color& m) { return m == color; });
if (color_it == m_colors.end()) {
m_colors.emplace_back(color);
color_it = std::prev(m_colors.end());
}
return std::distance(m_colors.begin(), color_it);
}
};
const float ToolpathsObjExporter::Cap_Rounding_Factor = 0.25f;
void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
{
if (filename == nullptr)
return;
if (!has_data())
return;
wxBusyCursor busy;
ToolpathsObjExporter exporter(m_viewer);
exporter.export_to(filename);
}
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
//BBS: update force previewing logic
if (force_previewing)
m_shells.previewing = force_previewing;
//already loaded
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": already loaded, print=%1% print_id=%2%, print_modify_count=%3%, force_previewing %4%")%(&print) %m_shells.print_id %m_shells.print_modify_count %force_previewing;
return;
}
//reset shell firstly
reset_shell();
//BBS: move behind of reset_shell, to clear previous shell for empty plate
if (print.objects().empty()) {
// no shells, return
return;
}
// adds objects' volumes
// BBS: fix the issue that object_idx is not assigned as index of Model.objects array
int object_count = 0;
const ModelObjectPtrs& model_objs = wxGetApp().model().objects;
for (const PrintObject* obj : print.objects()) {
const ModelObject* model_obj = obj->model_object();
int object_idx = -1;
for (int idx = 0; idx < model_objs.size(); idx++) {
if (model_objs[idx]->id() == model_obj->id()) {
object_idx = idx;
break;
}
}
// BBS: object may be deleted when this method is called when deleting an object
if (object_idx == -1)
continue;
std::vector<int> instance_ids(model_obj->instances.size());
//BBS: only add the printable instance
int instance_index = 0;
for (int i = 0; i < (int)model_obj->instances.size(); ++i) {
//BBS: only add the printable instance
if (model_obj->instances[i]->is_printable())
instance_ids[instance_index++] = i;
}
instance_ids.resize(instance_index);
size_t current_volumes_count = m_shells.volumes.volumes.size();
m_shells.volumes.load_object(model_obj, object_idx, instance_ids, "object", initialized, false);
// adjust shells' z if raft is present
const SlicingParameters& slicing_parameters = obj->slicing_parameters();
if (slicing_parameters.object_print_z_min != 0.0) {
const Vec3d z_offset = slicing_parameters.object_print_z_min * Vec3d::UnitZ();
for (size_t i = current_volumes_count; i < m_shells.volumes.volumes.size(); ++i) {
GLVolume* v = m_shells.volumes.volumes[i];
v->set_volume_offset(v->get_volume_offset() + z_offset);
}
}
object_count++;
}
// Orca: disable wipe tower shell
// if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) {
// // BBS: adds wipe tower's volume
// std::vector<unsigned int> print_extruders = print.extruders(true);
// int extruders_count = print_extruders.size();
// const double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2);
// const PrintConfig& config = print.config();
// if (config.enable_prime_tower &&
// (print.enable_timelapse_print() || (extruders_count > 1 && (config.print_sequence == PrintSequence::ByLayer)))) {
// const float depth = print.wipe_tower_data(extruders_count).depth;
// const float brim_width = print.wipe_tower_data(extruders_count).brim_width;
// int plate_idx = print.get_plate_index();
// Vec3d plate_origin = print.get_plate_origin();
// double wipe_tower_x = config.wipe_tower_x.get_at(plate_idx) + plate_origin(0);
// double wipe_tower_y = config.wipe_tower_y.get_at(plate_idx) + plate_origin(1);
// m_shells.volumes.load_wipe_tower_preview(1000, wipe_tower_x, wipe_tower_y, config.prime_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
// !print.is_step_done(psWipeTower), brim_width, initialized);
// }
// }
// remove modifiers
while (true) {
GLVolumePtrs::iterator it = std::find_if(m_shells.volumes.volumes.begin(), m_shells.volumes.volumes.end(), [](GLVolume* volume) { return volume->is_modifier; });
if (it != m_shells.volumes.volumes.end()) {
delete (*it);
m_shells.volumes.volumes.erase(it);
}
else
break;
}
for (GLVolume* volume : m_shells.volumes.volumes) {
volume->zoom_to_volumes = false;
volume->color.a(0.5f);
volume->force_native_color = true;
volume->set_render_color();
//BBS: add shell bounding box logic
m_shell_bounding_box.merge(volume->transformed_bounding_box());
}
//BBS: always load shell when preview
m_shells.print_id = print.id().id;
m_shells.print_modify_count = print.get_modified_count();
m_shells.previewing = true;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
}
void GCodeViewer::render_toolpaths()
{
const Camera& camera = wxGetApp().plater()->get_camera();
const libvgcode::Mat4x4 converted_view_matrix = libvgcode::convert(static_cast<Matrix4f>(camera.get_view_matrix().matrix().cast<float>()));
const libvgcode::Mat4x4 converted_projetion_matrix = libvgcode::convert(static_cast<Matrix4f>(camera.get_projection_matrix().matrix().cast<float>()));
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
m_viewer.set_cog_marker_scale_factor(m_cog_marker_fixed_screen_size ? 10.0f * m_cog_marker_size * camera.get_inv_zoom() : m_cog_marker_size);
m_viewer.set_tool_marker_scale_factor(m_tool_marker_fixed_screen_size ? 10.0f * m_tool_marker_size * camera.get_inv_zoom() : m_tool_marker_size);
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
m_viewer.render(converted_view_matrix, converted_projetion_matrix);
#if ENABLE_NEW_GCODE_VIEWER_DEBUG
if (is_legend_shown()) {
ImGuiWrapper& imgui = *wxGetApp().imgui();
const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
ImGuiPureWrap::set_next_window_pos(static_cast<float>(cnv_size.get_width()), 0.0f, ImGuiCond_Always, 1.0f, 0.0f);
ImGuiPureWrap::begin(std::string("LibVGCode Viewer Debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
if (ImGui::BeginTable("Data", 2)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "# vertices");
ImGui::TableSetColumnIndex(1);
ImGuiPureWrap::text(std::to_string(m_viewer.get_vertices_count()));
ImGui::Separator();
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "cpu memory");
ImGui::TableSetColumnIndex(1);
ImGuiPureWrap::text(format_memsize(m_viewer.get_used_cpu_memory()));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "gpu memory");
ImGui::TableSetColumnIndex(1);
ImGuiPureWrap::text(format_memsize(m_viewer.get_used_gpu_memory()));
ImGui::Separator();
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "layers range");
ImGui::TableSetColumnIndex(1);
const libvgcode::Interval& layers_range = m_viewer.get_layers_view_range();
ImGuiPureWrap::text(std::to_string(layers_range[0]) + " - " + std::to_string(layers_range[1]));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "view range (enabled)");
ImGui::TableSetColumnIndex(1);
const libvgcode::Interval& enabled_view_range = m_viewer.get_view_enabled_range();
ImGuiPureWrap::text(std::to_string(enabled_view_range[0]) + " - " + std::to_string(enabled_view_range[1]) + " | " +
std::to_string(m_viewer.get_vertex_at(enabled_view_range[0]).gcode_id) + " - " +
std::to_string(m_viewer.get_vertex_at(enabled_view_range[1]).gcode_id));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "view range (visible)");
ImGui::TableSetColumnIndex(1);
const libvgcode::Interval& visible_view_range = m_viewer.get_view_visible_range();
ImGuiPureWrap::text(std::to_string(visible_view_range[0]) + " - " + std::to_string(visible_view_range[1]) + " | " +
std::to_string(m_viewer.get_vertex_at(visible_view_range[0]).gcode_id) + " - " +
std::to_string(m_viewer.get_vertex_at(visible_view_range[1]).gcode_id));
auto add_range_property_row = [&imgui](const std::string& label, const std::array<float, 2>& range) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, label);
ImGui::TableSetColumnIndex(1);
char buf[128];
sprintf(buf, "%.3f - %.3f", range[0], range[1]);
ImGuiPureWrap::text(buf);
};
add_range_property_row("height range", m_viewer.get_color_range(libvgcode::EViewType::Height).get_range());
add_range_property_row("width range", m_viewer.get_color_range(libvgcode::EViewType::Width).get_range());
add_range_property_row("speed range", m_viewer.get_color_range(libvgcode::EViewType::Speed).get_range());
add_range_property_row("fan speed range", m_viewer.get_color_range(libvgcode::EViewType::FanSpeed).get_range());
add_range_property_row("temperature range", m_viewer.get_color_range(libvgcode::EViewType::Temperature).get_range());
add_range_property_row("volumetric rate range", m_viewer.get_color_range(libvgcode::EViewType::VolumetricFlowRate).get_range());
add_range_property_row("layer time linear range", m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLinear).get_range());
add_range_property_row("layer time logarithmic range", m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLogarithmic).get_range());
ImGui::EndTable();
}
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ImGui::Separator();
if (ImGui::BeginTable("Cog", 2)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Cog marker scale factor");
ImGui::TableSetColumnIndex(1);
ImGuiPureWrap::text(std::to_string(get_cog_marker_scale_factor()));
ImGui::EndTable();
}
ImGui::Separator();
if (ImGui::BeginTable("Tool", 2)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Tool marker scale factor");
ImGui::TableSetColumnIndex(1);
ImGuiPureWrap::text(std::to_string(m_viewer.get_tool_marker_scale_factor()));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Tool marker z offset");
ImGui::TableSetColumnIndex(1);
float tool_z_offset = m_viewer.get_tool_marker_offset_z();
if (imgui.slider_float("##ToolZOffset", &tool_z_offset, 0.0f, 1.0f))
m_viewer.set_tool_marker_offset_z(tool_z_offset);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Tool marker color");
ImGui::TableSetColumnIndex(1);
const libvgcode::Color& color = m_viewer.get_tool_marker_color();
std::array<float, 3> c = { static_cast<float>(color[0]) / 255.0f, static_cast<float>(color[1]) / 255.0f, static_cast<float>(color[2]) / 255.0f };
if (ImGui::ColorPicker3("##ToolColor", c.data())) {
m_viewer.set_tool_marker_color({ static_cast<uint8_t>(c[0] * 255.0f),
static_cast<uint8_t>(c[1] * 255.0f),
static_cast<uint8_t>(c[2] * 255.0f) });
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Tool marker alpha");
ImGui::TableSetColumnIndex(1);
float tool_alpha = m_viewer.get_tool_marker_alpha();
if (imgui.slider_float("##ToolAlpha", &tool_alpha, 0.25f, 0.75f))
m_viewer.set_tool_marker_alpha(tool_alpha);
ImGui::EndTable();
}
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ImGui::Separator();
if (ImGui::BeginTable("Radii", 2)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Travels radius");
ImGui::TableSetColumnIndex(1);
float travels_radius = m_viewer.get_travels_radius();
ImGui::SetNextItemWidth(200.0f);
if (imgui.slider_float("##TravelRadius", &travels_radius, 0.05f, 0.5f))
m_viewer.set_travels_radius(travels_radius);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, "Wipes radius");
ImGui::TableSetColumnIndex(1);
float wipes_radius = m_viewer.get_wipes_radius();
ImGui::SetNextItemWidth(200.0f);
if (imgui.slider_float("##WipesRadius", &wipes_radius, 0.05f, 0.5f))
m_viewer.set_wipes_radius(wipes_radius);
ImGui::EndTable();
}
imgui.end();
}
#endif // ENABLE_NEW_GCODE_VIEWER_DEBUG
}
void GCodeViewer::render_shells(int canvas_width, int canvas_height)
{
//BBS: add shell previewing logic
if ((!m_shells.previewing && !m_shells.visible) || m_shells.volumes.empty())
//if (!m_shells.visible || m_shells.volumes.empty())
return;
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
glsafe(::glDepthMask(GL_FALSE));
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("z_far", camera.get_far_z());
shader->set_uniform("z_near", camera.get_near_z());
m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
shader->set_uniform("emission_factor", 0.0f);
shader->stop_using();
glsafe(::glDepthMask(GL_TRUE));
}
//BBS
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
if (!show)
return;
for (auto gcode_result : gcode_result_list) {
if (gcode_result->moves.size() == 0)
return;
}
ImGuiWrapper& imgui = *wxGetApp().imgui();
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f * m_scale); // ORCA add window rounding to modernize / match style
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0, 10.0 * m_scale));
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.0f, 1.0f, 1.0f, 0.6f));
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(340.f * m_scale * imgui.scaled(1.0f / 15.0f), 0));
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), 0, ImVec2(0.5f, 0.5f));
ImGui::Begin(_L("Statistics of All Plates").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
std::vector<float> filament_diameters = gcode_result_list.front()->filament_diameters;
std::vector<float> filament_densities = gcode_result_list.front()->filament_densities;
std::vector<ColorRGBA> filament_colors;
decode_colors(wxGetApp().plater()->get_extruder_colors_from_plater_config(gcode_result_list.back()), filament_colors);
for (int i = 0; i < filament_colors.size(); i++) {
filament_colors[i] = adjust_color_for_rendering(filament_colors[i]);
}
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
float window_padding = 4.0f * m_scale;
const float icon_size = ImGui::GetTextLineHeight() * 0.7;
std::map<std::string, float> offsets;
std::map<int, double> model_volume_of_extruders_all_plates; // map<extruder_idx, volume>
std::map<int, double> flushed_volume_of_extruders_all_plates; // map<extruder_idx, flushed volume>
std::map<int, double> wipe_tower_volume_of_extruders_all_plates; // map<extruder_idx, flushed volume>
std::map<int, double> support_volume_of_extruders_all_plates; // map<extruder_idx, flushed volume>
std::vector<double> model_used_filaments_m_all_plates;
std::vector<double> model_used_filaments_g_all_plates;
std::vector<double> flushed_filaments_m_all_plates;
std::vector<double> flushed_filaments_g_all_plates;
std::vector<double> wipe_tower_used_filaments_m_all_plates;
std::vector<double> wipe_tower_used_filaments_g_all_plates;
std::vector<double> support_used_filaments_m_all_plates;
std::vector<double> support_used_filaments_g_all_plates;
float total_time_all_plates = 0.0f;
float total_cost_all_plates = 0.0f;
bool show_detailed_statistics_page = false;
struct ColumnData {
enum {
Model = 1,
Flushed = 2,
WipeTower = 4,
Support = 1 << 3,
};
};
int displayed_columns = 0;
auto max_width = [](const std::vector<std::string>& items, const std::string& title, float extra_size = 0.0f) {
float ret = ImGui::CalcTextSize(title.c_str()).x;
for (const std::string& item : items) {
ret = std::max(ret, extra_size + ImGui::CalcTextSize(item.c_str()).x);
}
return ret;
};
auto calculate_offsets = [max_width, window_padding](const std::vector<std::pair<std::string, std::vector<::string>>>& title_columns, float extra_size = 0.0f) {
const ImGuiStyle& style = ImGui::GetStyle();
std::vector<float> offsets;
offsets.push_back(max_width(title_columns[0].second, title_columns[0].first, extra_size) + 3.0f * style.ItemSpacing.x + style.WindowPadding.x);
for (size_t i = 1; i < title_columns.size() - 1; i++)
offsets.push_back(offsets.back() + max_width(title_columns[i].second, title_columns[i].first) + style.ItemSpacing.x);
if (title_columns.back().first == _u8L("Display"))
offsets.back() = ImGui::GetWindowWidth() - ImGui::CalcTextSize(_u8L("Display").c_str()).x - ImGui::GetFrameHeight() / 2 - 2 * window_padding;
float average_col_width = ImGui::GetWindowWidth() / static_cast<float>(title_columns.size());
std::vector<float> ret;
ret.push_back(0);
for (size_t i = 1; i < title_columns.size(); i++) {
ret.push_back(std::max(offsets[i - 1], i * average_col_width));
}
return ret;
};
auto append_item = [icon_size, &imgui, imperial_units, &window_padding, &draw_list, this](const ColorRGBA& color, const std::vector<std::pair<std::string, float>>& columns_offsets)
{
// render icon
ImVec2 pos = ImVec2(ImGui::GetCursorScreenPos().x + window_padding * 3, ImGui::GetCursorScreenPos().y);
draw_list->AddRectFilled({ pos.x + 1.0f * m_scale, pos.y + 3.0f * m_scale }, { pos.x + icon_size - 1.0f * m_scale, pos.y + icon_size + 1.0f * m_scale },
ImGuiWrapper::to_ImU32(color));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0 * m_scale, 6.0 * m_scale));
// render selectable
ImGui::Dummy({ 0.0, 0.0 });
ImGui::SameLine();
// render column item
{
float dummy_size = ImGui::GetStyle().ItemSpacing.x + icon_size;
ImGui::SameLine(dummy_size);
imgui.text(columns_offsets[0].first);
for (auto i = 1; i < columns_offsets.size(); i++) {
ImGui::SameLine(columns_offsets[i].second);
imgui.text(columns_offsets[i].first);
}
}
ImGui::PopStyleVar(1);
};
auto append_headers = [&imgui](const std::vector<std::pair<std::string, float>>& title_offsets) {
for (size_t i = 0; i < title_offsets.size(); i++) {
ImGui::SameLine(title_offsets[i].second);
imgui.bold_text(title_offsets[i].first);
}
ImGui::Separator();
};
auto get_used_filament_from_volume = [this, imperial_units, &filament_diameters, &filament_densities](double volume, int extruder_id) {
double koef = imperial_units ? 1.0 / GizmoObjectManipulation::in_to_mm : 0.001;
std::pair<double, double> ret = { koef * volume / (PI * sqr(0.5 * filament_diameters[extruder_id])),
volume * filament_densities[extruder_id] * 0.001 };
return ret;
};
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
// title and item data
{
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
for (auto plate : plate_list.get_nonempty_plate_list())
{
auto plate_print_statistics = plate->get_slice_result()->print_statistics;
auto plate_extruders = plate->get_extruders(true);
for (size_t extruder_id : plate_extruders) {
extruder_id -= 1;
if (plate_print_statistics.model_volumes_per_extruder.find(extruder_id) == plate_print_statistics.model_volumes_per_extruder.end())
model_volume_of_extruders_all_plates[extruder_id] += 0;
else {
double model_volume = plate_print_statistics.model_volumes_per_extruder.at(extruder_id);
model_volume_of_extruders_all_plates[extruder_id] += model_volume;
}
if (plate_print_statistics.flush_per_filament.find(extruder_id) == plate_print_statistics.flush_per_filament.end())
flushed_volume_of_extruders_all_plates[extruder_id] += 0;
else {
double flushed_volume = plate_print_statistics.flush_per_filament.at(extruder_id);
flushed_volume_of_extruders_all_plates[extruder_id] += flushed_volume;
}
if (plate_print_statistics.wipe_tower_volumes_per_extruder.find(extruder_id) == plate_print_statistics.wipe_tower_volumes_per_extruder.end())
wipe_tower_volume_of_extruders_all_plates[extruder_id] += 0;
else {
double wipe_tower_volume = plate_print_statistics.wipe_tower_volumes_per_extruder.at(extruder_id);
wipe_tower_volume_of_extruders_all_plates[extruder_id] += wipe_tower_volume;
}
if (plate_print_statistics.support_volumes_per_extruder.find(extruder_id) == plate_print_statistics.support_volumes_per_extruder.end())
support_volume_of_extruders_all_plates[extruder_id] += 0;
else {
double support_volume = plate_print_statistics.support_volumes_per_extruder.at(extruder_id);
support_volume_of_extruders_all_plates[extruder_id] += support_volume;
}
}
const PrintEstimatedStatistics::Mode& plate_time_mode = plate_print_statistics.modes[static_cast<size_t>(m_viewer.get_time_mode())];
total_time_all_plates += plate_time_mode.time;
Print *print;
plate->get_print((PrintBase **) &print, nullptr, nullptr);
total_cost_all_plates += print->print_statistics().total_cost;
}
for (auto it = model_volume_of_extruders_all_plates.begin(); it != model_volume_of_extruders_all_plates.end(); it++) {
auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(it->second, it->first);
if (model_used_filament_m != 0.0 || model_used_filament_g != 0.0)
displayed_columns |= ColumnData::Model;
model_used_filaments_m_all_plates.push_back(model_used_filament_m);
model_used_filaments_g_all_plates.push_back(model_used_filament_g);
}
for (auto it = flushed_volume_of_extruders_all_plates.begin(); it != flushed_volume_of_extruders_all_plates.end(); it++) {
auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(it->second, it->first);
if (flushed_filament_m != 0.0 || flushed_filament_g != 0.0)
displayed_columns |= ColumnData::Flushed;
flushed_filaments_m_all_plates.push_back(flushed_filament_m);
flushed_filaments_g_all_plates.push_back(flushed_filament_g);
}
for (auto it = wipe_tower_volume_of_extruders_all_plates.begin(); it != wipe_tower_volume_of_extruders_all_plates.end(); it++) {
auto [wipe_tower_filament_m, wipe_tower_filament_g] = get_used_filament_from_volume(it->second, it->first);
if (wipe_tower_filament_m != 0.0 || wipe_tower_filament_g != 0.0)
displayed_columns |= ColumnData::WipeTower;
wipe_tower_used_filaments_m_all_plates.push_back(wipe_tower_filament_m);
wipe_tower_used_filaments_g_all_plates.push_back(wipe_tower_filament_g);
}
for (auto it = support_volume_of_extruders_all_plates.begin(); it != support_volume_of_extruders_all_plates.end(); it++) {
auto [support_filament_m, support_filament_g] = get_used_filament_from_volume(it->second, it->first);
if (support_filament_m != 0.0 || support_filament_g != 0.0)
displayed_columns |= ColumnData::Support;
support_used_filaments_m_all_plates.push_back(support_filament_m);
support_used_filaments_g_all_plates.push_back(support_filament_g);
}
char buff[64];
double longest_str = 0.0;
for (auto i : model_used_filaments_g_all_plates) {
if (i > longest_str)
longest_str = i;
}
::sprintf(buff, "%.2f", longest_str);
std::vector<std::pair<std::string, std::vector<::string>>> title_columns;
if (displayed_columns & ColumnData::Model) {
title_columns.push_back({ _u8L("Filament"), {""} });
title_columns.push_back({ _u8L("Model"), {buff} });
}
if (displayed_columns & ColumnData::Support) {
title_columns.push_back({ _u8L("Support"), {buff} });
}
if (displayed_columns & ColumnData::Flushed) {
title_columns.push_back({ _u8L("Flushed"), {buff} });
}
if (displayed_columns & ColumnData::WipeTower) {
title_columns.push_back({ _u8L("Tower"), {buff} });
}
if ((displayed_columns & ~ColumnData::Model) > 0) {
title_columns.push_back({ _u8L("Total"), {buff} });
}
auto offsets_ = calculate_offsets(title_columns, icon_size);
std::vector<std::pair<std::string, float>> title_offsets;
for (int i = 0; i < offsets_.size(); i++) {
title_offsets.push_back({ title_columns[i].first, offsets_[i] });
offsets[title_columns[i].first] = offsets_[i];
}
append_headers(title_offsets);
}
// item
{
size_t i = 0;
for (auto it = model_volume_of_extruders_all_plates.begin(); it != model_volume_of_extruders_all_plates.end(); it++) {
if (i < model_used_filaments_m_all_plates.size() && i < model_used_filaments_g_all_plates.size()) {
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ std::to_string(it->first + 1), offsets[_u8L("Filament")]});
char buf[64];
double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1.0;
float column_sum_m = 0.0f;
float column_sum_g = 0.0f;
if (displayed_columns & ColumnData::Model) {
if ((displayed_columns & ~ColumnData::Model) > 0)
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i] / unit_conver);
else
::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i] / unit_conver);
columns_offsets.push_back({ buf, offsets[_u8L("Model")] });
column_sum_m += model_used_filaments_m_all_plates[i];
column_sum_g += model_used_filaments_g_all_plates[i];
}
if (displayed_columns & ColumnData::Support) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", support_used_filaments_m_all_plates[i], support_used_filaments_g_all_plates[i] / unit_conver);
columns_offsets.push_back({ buf, offsets[_u8L("Support")] });
column_sum_m += support_used_filaments_m_all_plates[i];
column_sum_g += support_used_filaments_g_all_plates[i];
}
if (displayed_columns & ColumnData::Flushed) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m_all_plates[i], flushed_filaments_g_all_plates[i] / unit_conver);
columns_offsets.push_back({ buf, offsets[_u8L("Flushed")] });
column_sum_m += flushed_filaments_m_all_plates[i];
column_sum_g += flushed_filaments_g_all_plates[i];
}
if (displayed_columns & ColumnData::WipeTower) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", wipe_tower_used_filaments_m_all_plates[i], wipe_tower_used_filaments_g_all_plates[i] / unit_conver);
columns_offsets.push_back({ buf, offsets[_u8L("Tower")] });
column_sum_m += wipe_tower_used_filaments_m_all_plates[i];
column_sum_g += wipe_tower_used_filaments_g_all_plates[i];
}
if ((displayed_columns & ~ColumnData::Model) > 0) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver);
columns_offsets.push_back({ buf, offsets[_u8L("Total")] });
}
append_item(filament_colors[it->first], columns_offsets);
}
i++;
}
ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.title(_u8L("Total Estimation"));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(_u8L("Total time") + ":");
ImGui::SameLine();
imgui.text(short_time(get_time_dhms(total_time_all_plates)));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(_u8L("Total cost") + ":");
ImGui::SameLine();
char buf[64];
::sprintf(buf, "%.2f", total_cost_all_plates);
imgui.text(buf);
}
ImGui::End();
ImGui::PopStyleColor(6);
ImGui::PopStyleVar(3);
return;
}
void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin)
{
if (!is_legend_shown())
return;
const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
ImGuiWrapper& imgui = *wxGetApp().imgui();
//BBS: GUI refactor: move to the right
imgui.set_next_window_pos(float(canvas_width - right_margin * m_scale), 4.0f * m_scale, ImGuiCond_Always, 1.0f, 0.0f); // ORCA add a small gap to top to create seperation with main toolbar
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f * m_scale); // ORCA add window rounding to modernize / match style
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0,0.0));
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.0f,1.0f,1.0f,0.6f));
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.59f, 0.53f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.59f, 0.53f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
ImGui::SetNextWindowBgAlpha(0.8f);
const float max_height = 0.75f * static_cast<float>(cnv_size.get_height());
const float child_height = 0.3333f * max_height;
ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height });
imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
enum class EItemType : unsigned char
{
Rect,
Circle,
Hexagon,
Line,
None
};
const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast<size_t>(m_viewer.get_time_mode())];
const libvgcode::EViewType curr_view_type = m_viewer.get_view_type();
const int curr_view_type_i = static_cast<int>(curr_view_type);
bool show_estimated_time = time_mode.time > 0.0f && (curr_view_type == libvgcode::EViewType::FeatureType ||
curr_view_type == libvgcode::EViewType::LayerTimeLinear || curr_view_type == libvgcode::EViewType::LayerTimeLogarithmic ||
(curr_view_type == libvgcode::EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()));
const float icon_size = ImGui::GetTextLineHeight() * 0.7;
//BBS GUI refactor
//const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight();
const float percent_bar_size = 0;
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 pos_rect = ImGui::GetCursorScreenPos();
float window_padding = 4.0f * m_scale;
// ORCA dont use background on top bar to give modern look
//draw_list->AddRectFilled(ImVec2(pos_rect.x,pos_rect.y - ImGui::GetStyle().WindowPadding.y),
//ImVec2(pos_rect.x + ImGui::GetWindowWidth() + ImGui::GetFrameHeight(),pos_rect.y + ImGui::GetFrameHeight() + window_padding * 2.5),
//ImGui::GetColorU32(ImVec4(0,0,0,0.3)));
auto append_item = [icon_size, &imgui, imperial_units, &window_padding, &draw_list, this](
EItemType type,
const ColorRGBA& color,
const std::vector<std::pair<std::string, float>>& columns_offsets,
bool checkbox = true,
float checkbox_pos = 0.f, // ORCA use calculated value for eye icon. Aligned to "Display" header or end of combo box
bool visible = true,
std::function<void()> callback = nullptr)
{
// render icon
ImVec2 pos = ImVec2(ImGui::GetCursorScreenPos().x + window_padding * 3, ImGui::GetCursorScreenPos().y);
switch (type) {
default:
case EItemType::Rect: {
draw_list->AddRectFilled({ pos.x + 1.0f * m_scale, pos.y + 3.0f * m_scale }, { pos.x + icon_size - 1.0f * m_scale, pos.y + icon_size + 1.0f * m_scale },
ImGuiWrapper::to_ImU32(color));
break;
}
case EItemType::Circle: {
ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size + 5.0f));
draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGuiWrapper::to_ImU32(color), 16);
break;
}
case EItemType::Hexagon: {
ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size + 5.0f));
draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGuiWrapper::to_ImU32(color), 6);
break;
}
case EItemType::Line: {
draw_list->AddLine({ pos.x + 1, pos.y + icon_size + 2 }, { pos.x + icon_size - 1, pos.y + 4 }, ImGuiWrapper::to_ImU32(color), 3.0f);
break;
case EItemType::None:
break;
}
}
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0 * m_scale, 6.0 * m_scale));
// BBS render selectable
ImGui::Dummy({ 0.0, 0.0 });
ImGui::SameLine();
if (callback) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * m_scale);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0 * m_scale, 0.0));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(1.00f, 0.68f, 0.26f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(1.00f, 0.68f, 0.26f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
float max_height = 0.f;
for (auto column_offset : columns_offsets) {
if (ImGui::CalcTextSize(column_offset.first.c_str()).y > max_height)
max_height = ImGui::CalcTextSize(column_offset.first.c_str()).y;
}
bool b_menu_item = ImGui::BBLMenuItem(("##" + columns_offsets[0].first).c_str(), nullptr, false, true, max_height);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(3);
if (b_menu_item)
callback();
if (checkbox) {
// ORCA replace checkboxes with eye icon
// Use calculated position from argument. this method has predictable result compared to alingning button using window width
// fixes slowly resizing window and endlessly expanding window when there is a miscalculation on position
ImGui::SameLine(checkbox_pos);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0, 0.0)); // ensure no padding active
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0, 0.0)); // ensure no item spacing active
ImGui::Text(into_u8(visible ? ImGui::VisibleIcon : ImGui::HiddenIcon).c_str(), ImVec2(16 * m_scale, 16 * m_scale));
ImGui::PopStyleVar(2);
}
}
// BBS render column item
{
if(callback && !checkbox && !visible)
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(172 / 255.0f, 172 / 255.0f, 172 / 255.0f, 1.00f));
float dummy_size = type == EItemType::None ? window_padding * 3 : ImGui::GetStyle().ItemSpacing.x + icon_size;
ImGui::SameLine(dummy_size);
imgui.text(columns_offsets[0].first);
for (auto i = 1; i < columns_offsets.size(); i++) {
ImGui::SameLine(columns_offsets[i].second);
imgui.text(columns_offsets[i].first);
}
if (callback && !checkbox && !visible)
ImGui::PopStyleColor(1);
}
ImGui::PopStyleVar(1);
};
auto append_range = [append_item](const libvgcode::ColorRange& range, unsigned int decimals) {
auto append_range_item = [append_item, &range](int i, float value, unsigned int decimals) {
char buf[1024];
::sprintf(buf, "%.*f", decimals, value);
append_item(EItemType::Rect, libvgcode::convert(range.get_palette()[i]), { { buf , 0} });
};
std::vector<float> values = range.get_values();
if (values.size() == 1)
// single item use case
append_range_item(0, values.front(), decimals);
else if (values.size() == 2) {
append_range_item(static_cast<int>(range.get_palette().size()) - 1, values.back(), decimals);
append_range_item(0, values.front(), decimals);
}
else {
for (int i = static_cast<int>(range.get_palette().size()) - 1; i >= 0; --i) {
append_range_item(i, values[i], decimals);
}
}
};
auto append_headers = [&imgui, window_padding, this](const std::vector<std::pair<std::string, float>>& title_offsets) {
for (size_t i = 0; i < title_offsets.size(); i++) {
if (title_offsets[i].first == _u8L("Display")) { // ORCA Hide Display header
ImGui::SameLine(title_offsets[i].second);
ImGui::Dummy({16.f * m_scale, 1}); // 16(icon_size)
continue;
}
ImGui::SameLine(title_offsets[i].second);
imgui.bold_text(title_offsets[i].first);
}
// Ensure right padding
ImGui::SameLine();
ImGui::Dummy({window_padding, 1});
ImGui::Separator();
};
auto max_width = [](const std::vector<std::string>& items, const std::string& title, float extra_size = 0.0f) {
float ret = ImGui::CalcTextSize(title.c_str()).x;
for (const std::string& item : items) {
ret = std::max(ret, extra_size + ImGui::CalcTextSize(item.c_str()).x);
}
return ret;
};
auto calculate_offsets = [&imgui, max_width, window_padding, this](const std::vector<std::pair<std::string, std::vector<::string>>>& title_columns, float extra_size = 0.0f) {
const ImGuiStyle& style = ImGui::GetStyle();
std::vector<float> offsets;
// ORCA increase spacing for more readable format. Using direct number requires much less code change in here. GetTextLineHeight for additional spacing for icon_size
offsets.push_back(max_width(title_columns[0].second, title_columns[0].first, extra_size) + 12.f * m_scale + ImGui::GetTextLineHeight());
for (size_t i = 1; i < title_columns.size() - 1; i++) // ORCA dont add extra spacing after icon / "Display" header
offsets.push_back(offsets.back() + max_width(title_columns[i].second, title_columns[i].first) + ((title_columns[i].first == _u8L("Display") ? 0 : 12.f) * m_scale));
if (title_columns.back().first == _u8L("Display") && title_columns.size() > 2)
offsets[title_columns.size() - 2] -= 3.f; // ORCA reduce spacing after previous header
float average_col_width = ImGui::GetWindowWidth() / static_cast<float>(title_columns.size());
std::vector<float> ret;
ret.push_back(0);
for (size_t i = 1; i < title_columns.size(); i++) {
ret.push_back(std::max(offsets[i - 1], i * average_col_width));
}
return ret;
};
auto color_print_ranges = [this](unsigned char extruder_id, const std::vector<CustomGCode::Item>& custom_gcode_per_print_z) {
std::vector<std::pair<ColorRGBA, std::pair<double, double>>> ret;
ret.reserve(custom_gcode_per_print_z.size());
for (const auto& item : custom_gcode_per_print_z) {
if (extruder_id + 1 != static_cast<unsigned char>(item.extruder))
continue;
if (item.type != ColorChange)
continue;
const std::vector<float> zs = m_viewer.get_layers_zs();
auto lower_b = std::lower_bound(zs.begin(), zs.end(),
static_cast<float>(item.print_z - epsilon()));
if (lower_b == zs.end())
continue;
const double current_z = *lower_b;
const double previous_z = (lower_b == zs.begin()) ? 0.0 : *(--lower_b);
// to avoid duplicate values, check adding values
if (ret.empty() || !(ret.back().second.first == previous_z && ret.back().second.second == current_z))
{
ColorRGBA color;
decode_color(item.color, color);
ret.push_back({ color, { previous_z, current_z } });
}
}
return ret;
};
auto upto_label = [](double z) {
char buf[64];
::sprintf(buf, "%.2f", z);
return _u8L("up to") + " " + std::string(buf) + " " + "mm";
};
auto above_label = [](double z) {
char buf[64];
::sprintf(buf, "%.2f", z);
return _u8L("above") + " " + std::string(buf) + " " + "mm";
};
auto fromto_label = [](double z1, double z2) {
char buf1[64];
::sprintf(buf1, "%.2f", z1);
char buf2[64];
::sprintf(buf2, "%.2f", z2);
return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + "mm";
};
auto role_time_and_percent = [this, time_mode](libvgcode::EGCodeExtrusionRole role) {
const float time = m_viewer.get_extrusion_role_estimated_time(role);
return std::make_pair(time, time / time_mode.time);
};
auto travel_time_and_percent = [this, time_mode]() {
const float time = m_viewer.get_travels_estimated_time();
return std::make_pair(time, time / time_mode.time);
};
auto used_filament_per_role = [this, imperial_units](ExtrusionRole role) {
auto it = m_print_statistics.used_filaments_per_role.find(role);
if (it == m_print_statistics.used_filaments_per_role.end())
return std::make_pair(0.0, 0.0);
double koef = imperial_units ? GizmoObjectManipulation::in_to_mm / 1000.0 : 1.0;
double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1;
return std::make_pair(it->second.first / koef, it->second.second / unit_conver);
};
// get used filament (meters and grams) from used volume in respect to the active extruder
auto get_used_filament_from_volume = [this, imperial_units](double volume, int extruder_id) {
double koef = imperial_units ? 1.0 / GizmoObjectManipulation::in_to_mm : 0.001;
std::pair<double, double> ret = { koef * volume / (PI * sqr(0.5 * m_filament_diameters[extruder_id])),
volume * m_filament_densities[extruder_id] * 0.001 };
return ret;
};
//BBS display Color Scheme
ImGui::Dummy({ window_padding, window_padding });
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine(window_padding * 2); // ORCA Ignores item spacing to get perfect window margins since since this part uses dummies for window padding
std::wstring btn_name;
if (m_fold)
btn_name = ImGui::UnfoldButtonIcon;
else
btn_name = ImGui::FoldButtonIcon;
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(84 / 255.f, 84 / 255.f, 90 / 255.f, 1.f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(84 / 255.f, 84 / 255.f, 90 / 255.f, 1.f));
float calc_padding = (ImGui::GetFrameHeight() - 16 * m_scale) / 2; // ORCA calculated padding for 16x16 icon
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(calc_padding, calc_padding)); // ORCA Center icon with frame padding
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f * m_scale); // ORCA Match button style with combo box
float button_width = 16 * m_scale + calc_padding * 2; // ORCA match buttons height with combo box
if (ImGui::Button(into_u8(btn_name).c_str(), ImVec2(button_width, button_width))) {
m_fold = !m_fold;
}
ImGui::SameLine();
const wchar_t gCodeToggle = ImGui::gCodeButtonIcon;
if (ImGui::Button(into_u8(gCodeToggle).c_str(), ImVec2(button_width, button_width))) {
wxGetApp().toggle_show_gcode_window();
wxGetApp().plater()->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
}
ImGui::PopStyleColor(3);
ImGui::PopStyleVar(2);
//imgui.bold_text(_u8L("Color Scheme"));
push_combo_style();
ImGui::SameLine();
const char* view_type_value = view_type_items_str[m_view_type_sel].c_str();
ImGuiComboFlags flags = ImGuiComboFlags_HeightLargest; // ORCA allow to fit all items to prevent scrolling on reaching last elements
if (ImGui::BBLBeginCombo("", view_type_value, flags)) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f);
for (int i = 0; i < view_type_items_str.size(); i++) {
const bool is_selected = (m_view_type_sel == i);
if (ImGui::BBLSelectable(view_type_items_str[i].c_str(), is_selected)) {
m_fold = false;
m_view_type_sel = i;
set_view_type(view_type_items[m_view_type_sel]);
reset_visible(view_type_items[m_view_type_sel]);
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::PopStyleVar(1);
ImGui::EndCombo();
}
pop_combo_style();
ImGui::SameLine(0, window_padding); // ORCA Without (0,window_padding) it adds unnecessary item spacing after combo box
// ORCA predictable_icon_pos helpful when window size determined by combo box.
float predictable_icon_pos = ImGui::GetCursorPosX() - icon_size - window_padding - ImGui::GetStyle().ItemSpacing.x - 1.f * m_scale; // 1 for border
ImGui::Dummy({ window_padding, window_padding });
ImGui::Dummy({ window_padding, window_padding }); // ORCA Matches top-bottom window paddings
float window_width = ImGui::GetWindowWidth(); // ORCA Store window width
if (m_fold) {
legend_height = ImGui::GetFrameHeight() + window_padding * 4; // ORCA using 4 instead 2 gives correct toolbar margins while its folded
ImGui::SameLine(window_width); // ORCA use stored window width while folded. This prevents annoying position change on fold/expand button
ImGui::Dummy({ 0, 0 });
imgui.end();
ImGui::PopStyleColor(6);
ImGui::PopStyleVar(2);
return;
}
// data used to properly align items in columns when showing time
std::vector<float> offsets;
std::vector<std::string> labels;
std::vector<std::string> times;
std::string travel_time;
std::vector<std::string> percents;
std::vector<std::string> used_filaments_length;
std::vector<std::string> used_filaments_weight;
std::string travel_percent;
std::vector<double> model_used_filaments_m;
std::vector<double> model_used_filaments_g;
double total_model_used_filament_m = 0, total_model_used_filament_g = 0;
std::vector<double> flushed_filaments_m;
std::vector<double> flushed_filaments_g;
double total_flushed_filament_m = 0, total_flushed_filament_g = 0;
std::vector<double> wipe_tower_used_filaments_m;
std::vector<double> wipe_tower_used_filaments_g;
double total_wipe_tower_used_filament_m = 0, total_wipe_tower_used_filament_g = 0;
std::vector<double> support_used_filaments_m;
std::vector<double> support_used_filaments_g;
double total_support_used_filament_m = 0, total_support_used_filament_g = 0;
struct ColumnData {
enum {
Model = 1,
Flushed = 2,
WipeTower = 4,
Support = 1 << 3,
};
};
int displayed_columns = 0;
std::map<std::string, float> color_print_offsets;
const PrintStatistics& ps = wxGetApp().plater()->get_partplate_list().get_current_fff_print().print_statistics();
double koef = imperial_units ? GizmoObjectManipulation::in_to_mm : 1000.0;
double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1;
std::vector<double> used_filaments_m;
std::vector<double> used_filaments_g;
// used filament statistics
for (size_t extruder_id : m_viewer.get_used_extruders_ids()) {
if (m_print_statistics.model_volumes_per_extruder.find(extruder_id) == m_print_statistics.model_volumes_per_extruder.end()) {
model_used_filaments_m.push_back(0.0);
model_used_filaments_g.push_back(0.0);
}
else {
double volume = m_print_statistics.model_volumes_per_extruder.at(extruder_id);
auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(volume, extruder_id);
model_used_filaments_m.push_back(model_used_filament_m);
model_used_filaments_g.push_back(model_used_filament_g);
total_model_used_filament_m += model_used_filament_m;
total_model_used_filament_g += model_used_filament_g;
displayed_columns |= ColumnData::Model;
}
}
for (size_t extruder_id : m_viewer.get_used_extruders_ids()) {
if (m_print_statistics.wipe_tower_volumes_per_extruder.find(extruder_id) == m_print_statistics.wipe_tower_volumes_per_extruder.end()) {
wipe_tower_used_filaments_m.push_back(0.0);
wipe_tower_used_filaments_g.push_back(0.0);
}
else {
double volume = m_print_statistics.wipe_tower_volumes_per_extruder.at(extruder_id);
auto [wipe_tower_used_filament_m, wipe_tower_used_filament_g] = get_used_filament_from_volume(volume, extruder_id);
wipe_tower_used_filaments_m.push_back(wipe_tower_used_filament_m);
wipe_tower_used_filaments_g.push_back(wipe_tower_used_filament_g);
total_wipe_tower_used_filament_m += wipe_tower_used_filament_m;
total_wipe_tower_used_filament_g += wipe_tower_used_filament_g;
displayed_columns |= ColumnData::WipeTower;
}
}
for (size_t extruder_id : m_viewer.get_used_extruders_ids()) {
if (m_print_statistics.flush_per_filament.find(extruder_id) == m_print_statistics.flush_per_filament.end()) {
flushed_filaments_m.push_back(0.0);
flushed_filaments_g.push_back(0.0);
}
else {
double volume = m_print_statistics.flush_per_filament.at(extruder_id);
auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(volume, extruder_id);
flushed_filaments_m.push_back(flushed_filament_m);
flushed_filaments_g.push_back(flushed_filament_g);
total_flushed_filament_m += flushed_filament_m;
total_flushed_filament_g += flushed_filament_g;
displayed_columns |= ColumnData::Flushed;
}
}
for (size_t extruder_id : m_viewer.get_used_extruders_ids()) {
if (m_print_statistics.support_volumes_per_extruder.find(extruder_id) == m_print_statistics.support_volumes_per_extruder.end()) {
support_used_filaments_m.push_back(0.0);
support_used_filaments_g.push_back(0.0);
}
else {
double volume = m_print_statistics.support_volumes_per_extruder.at(extruder_id);
auto [used_filament_m, used_filament_g] = get_used_filament_from_volume(volume, extruder_id);
support_used_filaments_m.push_back(used_filament_m);
support_used_filaments_g.push_back(used_filament_g);
total_support_used_filament_m += used_filament_m;
total_support_used_filament_g += used_filament_g;
displayed_columns |= ColumnData::Support;
}
}
// extrusion paths section -> title
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
switch (curr_view_type)
{
case libvgcode::EViewType::FeatureType:
{
// calculate offsets to align time/percentage data
char buffer[64];
const std::vector<libvgcode::EGCodeExtrusionRole>& roles = m_viewer.get_extrusion_roles();
for (libvgcode::EGCodeExtrusionRole role : roles) {
assert(static_cast<size_t>(role) < libvgcode::GCODE_EXTRUSION_ROLES_COUNT);
if (static_cast<size_t>(role) < libvgcode::GCODE_EXTRUSION_ROLES_COUNT) {
labels.push_back(_u8L(ExtrusionEntity::role_to_string(convert(role))));
auto [time, percent] = role_time_and_percent(role);
times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : "");
if (percent == 0) // ORCA remove % symbol from rows
::sprintf(buffer, "0");
else
percent > 0.001 ? ::sprintf(buffer, "%.1f", percent * 100) : ::sprintf(buffer, "<0.1");
percents.push_back(buffer);
auto [model_used_filament_m, model_used_filament_g] = used_filament_per_role(convert(role));
::sprintf(buffer, imperial_units ? "%.2fin" : "%.2fm", model_used_filament_m); // ORCA dont use spacing between value and unit
used_filaments_length.push_back(buffer);
::sprintf(buffer, imperial_units ? "%.2foz" : "%.2fg", model_used_filament_g); // ORCA dont use spacing between value and unit
used_filaments_weight.push_back(buffer);
}
}
//BBS: get travel time and percent
{
auto [time, percent] = travel_time_and_percent();
travel_time = (time > 0.0f) ? short_time(get_time_dhms(time)) : "";
if (percent == 0) // ORCA remove % symbol from rows
::sprintf(buffer, "0");
else
percent > 0.001 ? ::sprintf(buffer, "%.1f", percent * 100) : ::sprintf(buffer, "<0.1");
travel_percent = buffer;
}
// ORCA use % symbol for percentage and use "Usage" for "Used filaments"
offsets = calculate_offsets({ {_u8L("Line Type"), labels}, {_u8L("Time"), times}, {"%", percents}, {"", used_filaments_length}, {"", used_filaments_weight}, {_u8L("Display"), {""}}}, icon_size);
append_headers({{_u8L("Line Type"), offsets[0]}, {_u8L("Time"), offsets[1]}, {"%", offsets[2]}, {_u8L("Usage"), offsets[3]}, {_u8L("Display"), offsets[5]}});
break;
}
case libvgcode::EViewType::Height: { imgui.title(_u8L("Layer Height (mm)")); break; }
case libvgcode::EViewType::Width: { imgui.title(_u8L("Line Width (mm)")); break; }
case libvgcode::EViewType::Speed:
{
imgui.title(_u8L("Speed (mm/s)"));
break;
}
case libvgcode::EViewType::ActualSpeed:
{
imgui.title(_u8L("Actual Speed (mm/s)"));
break;
}
case libvgcode::EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; }
case libvgcode::EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; }
case libvgcode::EViewType::VolumetricFlowRate:
{ imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; }
case libvgcode::EViewType::ActualVolumetricFlowRate:
{ imgui.title(_u8L("Actual volumetric flow rate (mm³/s)")); break; }
case libvgcode::EViewType::LayerTimeLinear:
{ imgui.title(_u8L("Layer Time")); break; }
case libvgcode::EViewType::LayerTimeLogarithmic:
{ imgui.title(_u8L("Layer Time (log)")); break; }
case libvgcode::EViewType::Tool:
{
// calculate used filaments data
const size_t extruders_count = get_extruders_count();
used_filaments_m = std::vector<double>(extruders_count, 0.0);
used_filaments_g = std::vector<double>(extruders_count, 0.0);
const std::vector<uint8_t>& used_extruders_ids = m_viewer.get_used_extruders_ids();
for (uint8_t extruder_id : used_extruders_ids) {
if (m_print_statistics.model_volumes_per_extruder.find(extruder_id) == m_print_statistics.model_volumes_per_extruder.end())
continue;
double volume = m_print_statistics.model_volumes_per_extruder.at(extruder_id);
auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(volume, extruder_id);
model_used_filaments_m.push_back(model_used_filament_m);
model_used_filaments_g.push_back(model_used_filament_g);
}
offsets = calculate_offsets({ { "Extruder NNN", {""}}}, icon_size);
append_headers({ {_u8L("Filament"), offsets[0]}, {_u8L("Usage"), offsets[1]} });
break;
}
case libvgcode::EViewType::ColorPrint:
{
std::vector<std::string> total_filaments;
char buffer[64];
::sprintf(buffer, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", ps.total_used_filament / /*1000*/koef, ps.total_weight / unit_conver);
total_filaments.push_back(buffer);
std::vector<std::pair<std::string, std::vector<::string>>> title_columns;
if (displayed_columns & ColumnData::Model) {
title_columns.push_back({ _u8L("Filament"), {""} });
title_columns.push_back({ _u8L("Model"), total_filaments });
}
if (displayed_columns & ColumnData::Support) {
title_columns.push_back({ _u8L("Support"), total_filaments });
}
if (displayed_columns & ColumnData::Flushed) {
title_columns.push_back({ _u8L("Flushed"), total_filaments });
}
if (displayed_columns & ColumnData::WipeTower) {
title_columns.push_back({ _u8L("Tower"), total_filaments });
}
if ((displayed_columns & ~ColumnData::Model) > 0) {
title_columns.push_back({ _u8L("Total"), total_filaments });
}
title_columns.push_back({ _u8L("Display"), {""}}); // ORCA Add spacing for eye icon. used as color_print_offsets[_u8L("Display")]
auto offsets_ = calculate_offsets(title_columns, icon_size);
std::vector<std::pair<std::string, float>> title_offsets;
for (int i = 0; i < offsets_.size(); i++) {
title_offsets.push_back({ title_columns[i].first, offsets_[i] });
color_print_offsets[title_columns[i].first] = offsets_[i];
}
append_headers(title_offsets);
break;
}
default: { break; }
}
auto append_option_item = [this, append_item](libvgcode::EOptionType type, std::vector<float> offsets) {
auto append_option_item_with_type = [this, offsets, append_item](libvgcode::EOptionType type, const ColorRGBA& color, const std::string& label, bool visible) {
append_item(EItemType::Rect, color, {{ label , offsets[0] }}, true, offsets.back()/*ORCA checkbox_pos*/, visible, [this, type, visible]() {
m_viewer.toggle_option_visibility(type);
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
};
const bool visible = m_viewer.is_option_visible(type);
if (type == libvgcode::EOptionType::Travels) {
//BBS: only display travel time in FeatureType view
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), _u8L("Travel"), visible);
}
else if (type == libvgcode::EOptionType::Seams)
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Seams)), _u8L("Seams"), visible);
else if (type == libvgcode::EOptionType::Retractions)
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Retractions)), _u8L("Retract"), visible);
else if (type == libvgcode::EOptionType::Unretractions)
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Unretractions)), _u8L("Unretract"), visible);
else if (type == libvgcode::EOptionType::ToolChanges)
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::ToolChanges)), _u8L("Filament Changes"), visible);
else if (type == libvgcode::EOptionType::Wipes)
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Wipes)), _u8L("Wipe"), visible);
};
const libvgcode::EViewType new_view_type = m_viewer.get_view_type();
// extrusion paths section -> items
switch (new_view_type)
{
case libvgcode::EViewType::FeatureType:
{
auto roles = m_viewer.get_extrusion_roles();
for (size_t i = 0; i < roles.size(); ++i) {
libvgcode::EGCodeExtrusionRole role = roles[i];
if (role >= libvgcode::EGCodeExtrusionRole::COUNT)
continue;
const bool visible = m_viewer.is_extrusion_role_visible(role);
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ labels[i], offsets[0] });
columns_offsets.push_back({ times[i], offsets[1] });
columns_offsets.push_back({percents[i], offsets[2]});
columns_offsets.push_back({used_filaments_length[i], offsets[3]});
columns_offsets.push_back({used_filaments_weight[i], offsets[4]});
append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_extrusion_role_color(role)), columns_offsets,
true, offsets.back(), visible, [this, role, visible]() {
if (m_viewer.is_extrusion_role_visible(role) != visible) {
m_viewer.toggle_extrusion_role_visibility(role);
}
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
}
auto options = m_viewer.get_options();
for(auto item : options) {
if (item != libvgcode::EOptionType::Travels) {
append_option_item(item, offsets);
} else {
//BBS: show travel time in FeatureType view
const bool visible = m_viewer.is_option_visible(item);
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ _u8L("Travel"), offsets[0] });
columns_offsets.push_back({ travel_time, offsets[1] });
columns_offsets.push_back({ travel_percent, offsets[2] });
append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), columns_offsets, true, offsets.back()/*ORCA checkbox_pos*/, visible, [this, item, visible]() {
m_viewer.toggle_option_visibility(item);
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
}
}
break;
}
case libvgcode::EViewType::Height: { append_range(m_viewer.get_color_range(libvgcode::EViewType::Height), 2); break; }
case libvgcode::EViewType::Width: { append_range(m_viewer.get_color_range(libvgcode::EViewType::Width), 2); break; }
case libvgcode::EViewType::Speed: {
append_range(m_viewer.get_color_range(libvgcode::EViewType::Speed), 0);
ImGui::Spacing();
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
offsets = calculate_offsets({ { _u8L("Options"), { _u8L("Travel")}}, { _u8L("Display"), {""}} }, icon_size);
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
const bool travel_visible = m_viewer.is_option_visible(libvgcode::EOptionType::Travels);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f));
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
m_viewer.toggle_option_visibility(libvgcode::EOptionType::Travels);
// refresh(*m_gcode_result, wxGetApp().plater()->get_extruder_colors_from_plater_config(m_gcode_result));
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
ImGui::PopStyleVar(1);
break;
}
case libvgcode::EViewType::ActualSpeed: { append_range(m_viewer.get_color_range(libvgcode::EViewType::ActualSpeed), 0); break; } // TODO
case libvgcode::EViewType::FanSpeed: { append_range(m_viewer.get_color_range(libvgcode::EViewType::FanSpeed), 0); break; }
case libvgcode::EViewType::Temperature: { append_range(m_viewer.get_color_range(libvgcode::EViewType::Temperature), 0); break; }
case libvgcode::EViewType::LayerTimeLinear: { append_range(m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLinear), true); break; }
case libvgcode::EViewType::LayerTimeLogarithmic: { append_range(m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLogarithmic), true); break; }
case libvgcode::EViewType::VolumetricFlowRate: { append_range(m_viewer.get_color_range(libvgcode::EViewType::VolumetricFlowRate), 2); break; }
case libvgcode::EViewType::ActualVolumetricFlowRate: { append_range(m_viewer.get_color_range(libvgcode::EViewType::ActualVolumetricFlowRate), 2); break; }
case libvgcode::EViewType::Tool:
{
// shows only extruders actually used
char buf[64];
size_t i = 0;
const std::vector<uint8_t>& used_extruders_ids = m_viewer.get_used_extruders_ids();
for (uint8_t extruder_id : used_extruders_ids) {
::sprintf(buf, imperial_units ? "%.2f in %.2f g" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i]);
append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), { { _u8L("Extruder") + " " + std::to_string(extruder_id + 1), offsets[0]}, {buf, offsets[1]} });
// append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), _u8L("Extruder") + " " + std::to_string(extruder_id + 1),
// true, "", 0.0f, 0.0f, offsets, used_filaments_m[extruder_id], used_filaments_g[extruder_id]);
i++;
}
break;
}
case libvgcode::EViewType::ColorPrint: {
//BBS: replace model custom gcode with current plate custom gcode
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z;
size_t total_items = 1;
// BBS: no ColorChange type, use ToolChange
//const std::vector<uint8_t>& used_extruders_ids = m_viewer.get_used_extruders_ids();
//for (uint8_t extruder_id : used_extruders_ids) {
// total_items += color_print_ranges(extruder_id, custom_gcode_per_print_z).size();
//}
const bool need_scrollable = static_cast<float>(total_items) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height;
// add scrollable region, if needed
if (need_scrollable)
ImGui::BeginChild("color_prints", { -1.0f, child_height }, false);
// shows only extruders actually used
size_t i = 0;
const std::vector<uint8_t>& used_extruders_ids = m_viewer.get_used_extruders_ids();
for (auto extruder_idx : used_extruders_ids) { // TODO
const bool filament_visible = m_tools.m_tool_visibles[extruder_idx];
if (i < model_used_filaments_m.size() && i < model_used_filaments_g.size()) {
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ std::to_string(extruder_idx + 1), color_print_offsets[_u8L("Filament")]});
char buf[64];
float column_sum_m = 0.0f;
float column_sum_g = 0.0f;
if (displayed_columns & ColumnData::Model) {
if ((displayed_columns & ~ColumnData::Model) > 0)
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver);
else
::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] });
column_sum_m += model_used_filaments_m[i];
column_sum_g += model_used_filaments_g[i];
}
if (displayed_columns & ColumnData::Support) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", support_used_filaments_m[i], support_used_filaments_g[i] / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] });
column_sum_m += support_used_filaments_m[i];
column_sum_g += support_used_filaments_g[i];
}
if (displayed_columns & ColumnData::Flushed) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m[i], flushed_filaments_g[i] / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")]});
column_sum_m += flushed_filaments_m[i];
column_sum_g += flushed_filaments_g[i];
}
if (displayed_columns & ColumnData::WipeTower) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", wipe_tower_used_filaments_m[i], wipe_tower_used_filaments_g[i] / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] });
column_sum_m += wipe_tower_used_filaments_m[i];
column_sum_g += wipe_tower_used_filaments_g[i];
}
if ((displayed_columns & ~ColumnData::Model) > 0) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] });
}
float checkbox_pos = std::max(predictable_icon_pos, color_print_offsets[_u8L("Display")]); // ORCA prefer predictable_icon_pos when header not reacing end
append_item(EItemType::Rect, m_tools.m_tool_colors[extruder_idx], columns_offsets, true, checkbox_pos/*ORCA*/, filament_visible, [this, extruder_idx]() {
m_tools.m_tool_visibles[extruder_idx] = !m_tools.m_tool_visibles[extruder_idx];
update_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
}
i++;
}
if (need_scrollable)
ImGui::EndChild();
// Sum of all rows
char buf[64];
if (used_extruders_ids.size() > 1) {
// Separator
ImGuiWindow* window = ImGui::GetCurrentWindow();
const ImRect separator(ImVec2(window->Pos.x + window_padding * 3, window->DC.CursorPos.y), ImVec2(window->Pos.x + window->Size.x - window_padding * 3, window->DC.CursorPos.y + 1.0f));
ImGui::ItemSize(ImVec2(0.0f, 0.0f));
const bool item_visible = ImGui::ItemAdd(separator, 0);
window->DrawList->AddLine(separator.Min, ImVec2(separator.Max.x, separator.Min.y), ImGui::GetColorU32(ImGuiCol_Separator));
std::vector<std::pair<std::string, float>> columns_offsets;
columns_offsets.push_back({ _u8L("Total"), color_print_offsets[_u8L("Filament")]});
if (displayed_columns & ColumnData::Model) {
if ((displayed_columns & ~ColumnData::Model) > 0)
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver);
else
::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] });
}
if (displayed_columns & ColumnData::Support) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_support_used_filament_m, total_support_used_filament_g / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] });
}
if (displayed_columns & ColumnData::Flushed) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_flushed_filament_m, total_flushed_filament_g / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")] });
}
if (displayed_columns & ColumnData::WipeTower) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_wipe_tower_used_filament_m, total_wipe_tower_used_filament_g / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] });
}
if ((displayed_columns & ~ColumnData::Model) > 0) {
::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m,
(total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g) / unit_conver);
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] });
}
append_item(EItemType::None, m_tools.m_tool_colors[0], columns_offsets);
}
//BBS display filament change times
ImGui::Dummy({window_padding, window_padding});
ImGui::SameLine();
imgui.text(_u8L("Filament change times") + ":");
ImGui::SameLine();
::sprintf(buf, "%d", m_print_statistics.total_filamentchanges);
imgui.text(buf);
//BBS display cost
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(_u8L("Cost")+":");
ImGui::SameLine();
::sprintf(buf, "%.2f", ps.total_cost);
imgui.text(buf);
break;
}
default: { break; }
}
// partial estimated printing time section
if (new_view_type == libvgcode::EViewType::ColorPrint) {
using Times = std::pair<float, float>;
using TimesList = std::vector<std::pair<CustomGCode::Type, Times>>;
// helper structure containig the data needed to render the time items
struct PartialTime
{
enum class EType : unsigned char
{
Print,
ColorChange,
Pause
};
EType type;
int extruder_id;
ColorRGBA color1;
ColorRGBA color2;
Times times;
std::pair<double, double> used_filament {0.0f, 0.0f};
};
using PartialTimes = std::vector<PartialTime>;
auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector<double>& used_filaments) {
PartialTimes items;
//BBS: replace model custom gcode with current plate custom gcode
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z;
const size_t extruders_count = get_extruders_count();
std::vector<ColorRGBA> last_color(extruders_count);
for (size_t i = 0; i < extruders_count; ++i) {
last_color[i] = libvgcode::convert(m_viewer.get_tool_colors()[i]);
}
int last_extruder_id = 1;
int color_change_idx = 0;
for (const auto& time_rec : times) {
switch (time_rec.first)
{
case CustomGCode::PausePrint: {
auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; });
if (it != custom_gcode_per_print_z.end()) {
items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], ColorRGBA::BLACK(), time_rec.second });
items.push_back({ PartialTime::EType::Pause, it->extruder, ColorRGBA::BLACK(), ColorRGBA::BLACK(), time_rec.second });
custom_gcode_per_print_z.erase(it);
}
break;
}
case CustomGCode::ColorChange: {
auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; });
if (it != custom_gcode_per_print_z.end()) {
items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], ColorRGBA::BLACK(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], it->extruder - 1) });
ColorRGBA color;
decode_color(it->color, color);
items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], color, time_rec.second });
last_color[it->extruder - 1] = color;
last_extruder_id = it->extruder;
custom_gcode_per_print_z.erase(it);
}
else
items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], ColorRGBA::BLACK(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], last_extruder_id - 1) });
break;
}
default: { break; }
}
}
return items;
};
auto append_color_change = [&imgui](const ColorRGBA& color1, const ColorRGBA& color2, const std::array<float, 4>& offsets, const Times& times) {
imgui.text(_u8L("Color change"));
ImGui::SameLine();
float icon_size = ImGui::GetTextLineHeight();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetCursorScreenPos();
pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x;
draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f },
ImGuiWrapper::to_ImU32(color1));
pos.x += icon_size;
draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f },
ImGuiWrapper::to_ImU32(color2));
ImGui::SameLine(offsets[0]);
imgui.text(short_time(get_time_dhms(times.second - times.first)));
};
auto append_print = [&imgui, imperial_units](const ColorRGBA& color, const std::array<float, 4>& offsets, const Times& times, std::pair<double, double> used_filament) {
imgui.text(_u8L("Print"));
ImGui::SameLine();
float icon_size = ImGui::GetTextLineHeight();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetCursorScreenPos();
pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x;
draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f },
ImGuiWrapper::to_ImU32(color));
ImGui::SameLine(offsets[0]);
imgui.text(short_time(get_time_dhms(times.second)));
ImGui::SameLine(offsets[1]);
imgui.text(short_time(get_time_dhms(times.first)));
if (used_filament.first > 0.0f) {
char buffer[64];
ImGui::SameLine(offsets[2]);
::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", used_filament.first);
imgui.text(buffer);
ImGui::SameLine(offsets[3]);
::sprintf(buffer, "%.2f g", used_filament.second);
imgui.text(buffer);
}
};
PartialTimes partial_times = generate_partial_times(time_mode.custom_gcode_times, m_print_statistics.volumes_per_color_change);
if (!partial_times.empty()) {
labels.clear();
times.clear();
for (const PartialTime& item : partial_times) {
switch (item.type)
{
case PartialTime::EType::Print: { labels.push_back(_u8L("Print")); break; }
case PartialTime::EType::Pause: { labels.push_back(_u8L("Pause")); break; }
case PartialTime::EType::ColorChange: { labels.push_back(_u8L("Color change")); break; }
}
times.push_back(short_time(get_time_dhms(item.times.second)));
}
std::string longest_used_filament_string;
for (const PartialTime& item : partial_times) {
if (item.used_filament.first > 0.0f) {
char buffer[64];
::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", item.used_filament.first);
if (::strlen(buffer) > longest_used_filament_string.length())
longest_used_filament_string = buffer;
}
}
//offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), longest_used_filament_string }, 2.0f * icon_size);
//ImGui::Spacing();
//append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), _u8L("Used filament") }, offsets);
//const bool need_scrollable = static_cast<float>(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height;
//if (need_scrollable)
// // add scrollable region
// ImGui::BeginChild("events", { -1.0f, child_height }, false);
//for (const PartialTime& item : partial_times) {
// switch (item.type)
// {
// case PartialTime::EType::Print: {
// append_print(item.color1, offsets, item.times, item.used_filament);
// break;
// }
// case PartialTime::EType::Pause: {
// imgui.text(_u8L("Pause"));
// ImGui::SameLine(offsets[0]);
// imgui.text(short_time(get_time_dhms(item.times.second - item.times.first)));
// break;
// }
// case PartialTime::EType::ColorChange: {
// append_color_change(item.color1, item.color2, offsets, item.times);
// break;
// }
// }
//}
//if (need_scrollable)
// ImGui::EndChild();
}
}
// travel paths section
if (m_viewer.is_option_visible(libvgcode::EOptionType::Travels)) {
switch (m_viewer.get_view_type())
{
case libvgcode::EViewType::Speed:
case libvgcode::EViewType::Tool:
case libvgcode::EViewType::ColorPrint: {
break;
}
default: {
// BBS GUI:refactor
// title
//ImGui::Spacing();
//imgui.title(_u8L("Travel"));
//// items
//append_item(EItemType::Line, Travel_Colors[0], _u8L("Movement"));
//append_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion"));
//append_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction"));
break;
}
}
}
// wipe paths section
//if (m_buffers[buffer_id(EMoveType::Wipe)].visible) {
// switch (m_view_type)
// {
// case EViewType::Feedrate:
// case EViewType::Tool:
// case EViewType::ColorPrint: { break; }
// default: {
// // title
// ImGui::Spacing();
// ImGui::Dummy({ window_padding, window_padding });
// ImGui::SameLine();
// imgui.title(_u8L("Wipe"));
// // items
// append_item(EItemType::Line, Wipe_Color, { {_u8L("Wipe"), 0} });
// break;
// }
// }
//}
// auto any_option_available = [this]() {
// auto available = [this](EMoveType type) {
// const TBuffer& buffer = m_buffers[buffer_id(type)];
// return buffer.visible && buffer.has_data();
// };
//
// return available(EMoveType::Color_change) ||
// available(EMoveType::Custom_GCode) ||
// available(EMoveType::Pause_Print) ||
// available(EMoveType::Retract) ||
// available(EMoveType::Tool_change) ||
// available(EMoveType::Unretract) ||
// available(EMoveType::Seam);
// };
//auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) {
// const TBuffer& buffer = m_buffers[buffer_id(move_type)];
// if (buffer.visible && buffer.has_data())
// append_item(EItemType::Circle, Options_Colors[static_cast<unsigned int>(color)], text);
//};
/* BBS GUI refactor */
// options section
//if (any_option_available()) {
// // title
// ImGui::Spacing();
// imgui.title(_u8L("Options"));
// // items
// add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions"));
// add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions"));
// add_option(EMoveType::Seam, EOptionsColors::Seams, _u8L("Seams"));
// add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes"));
// add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes"));
// add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses"));
// add_option(EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom G-codes"));
//}
// settings section
bool has_settings = false;
has_settings |= !m_settings_ids.print.empty();
has_settings |= !m_settings_ids.printer.empty();
bool has_filament_settings = true;
has_filament_settings &= !m_settings_ids.filament.empty();
for (const std::string& fs : m_settings_ids.filament) {
has_filament_settings &= !fs.empty();
}
has_settings |= has_filament_settings;
//BBS: add only gcode mode
bool show_settings = m_only_gcode_in_preview; //wxGetApp().is_gcode_viewer();
show_settings &= (new_view_type == libvgcode::EViewType::FeatureType || new_view_type == libvgcode::EViewType::Tool);
show_settings &= has_settings;
if (show_settings) {
auto calc_offset = [this]() {
float ret = 0.0f;
if (!m_settings_ids.printer.empty())
ret = std::max(ret, ImGui::CalcTextSize((_u8L("Printer") + std::string(":")).c_str()).x);
if (!m_settings_ids.print.empty())
ret = std::max(ret, ImGui::CalcTextSize((_u8L("Print settings") + std::string(":")).c_str()).x);
if (!m_settings_ids.filament.empty()) {
for (unsigned char i : m_viewer.get_used_extruders_ids()) {
ret = std::max(ret, ImGui::CalcTextSize((_u8L("Filament") + " " + std::to_string(i + 1) + ":").c_str()).x);
}
}
if (ret > 0.0f)
ret += 2.0f * ImGui::GetStyle().ItemSpacing.x;
return ret;
};
ImGui::Spacing();
imgui.title(_u8L("Settings"));
float offset = calc_offset();
if (!m_settings_ids.printer.empty()) {
imgui.text(_u8L("Printer") + ":");
ImGui::SameLine(offset);
imgui.text(m_settings_ids.printer);
}
if (!m_settings_ids.print.empty()) {
imgui.text(_u8L("Print settings") + ":");
ImGui::SameLine(offset);
imgui.text(m_settings_ids.print);
}
if (!m_settings_ids.filament.empty()) {
for (unsigned char i : m_viewer.get_used_extruders_ids()) {
if (i < static_cast<unsigned char>(m_settings_ids.filament.size()) && !m_settings_ids.filament[i].empty()) {
std::string txt = _u8L("Filament");
txt += (m_viewer.get_used_extruders_count() == 1) ? ":" : " " + std::to_string(i + 1);
imgui.text(txt);
ImGui::SameLine(offset);
imgui.text(m_settings_ids.filament[i]);
}
}
}
}
// Custom G-code overview
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ?
wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes :
m_custom_gcode_per_print_z;
if (custom_gcode_per_print_z.size() != 0) {
float max_len = window_padding + 2 * ImGui::GetStyle().ItemSpacing.x;
ImGui::Spacing();
// Title Line
std::string cgcode_title_str = _u8L("Custom G-code");
std::string cgcode_layer_str = _u8L("Layer");
std::string cgcode_time_str = _u8L("Time");
// Types of custom gcode
std::string cgcode_pause_str = _u8L("Pause");
std::string cgcode_template_str= _u8L("Template");
std::string cgcode_toolchange_str = _u8L("Tool Change");
std::string cgcode_custom_str = _u8L("Custom");
std::string cgcode_unknown_str = _u8L("Unknown");
// Get longest String
max_len += std::max(ImGui::CalcTextSize(cgcode_title_str.c_str()).x,
std::max(ImGui::CalcTextSize(cgcode_pause_str.c_str()).x,
std::max(ImGui::CalcTextSize(cgcode_template_str.c_str()).x,
std::max(ImGui::CalcTextSize(cgcode_toolchange_str.c_str()).x,
std::max(ImGui::CalcTextSize(cgcode_custom_str.c_str()).x,
ImGui::CalcTextSize(cgcode_unknown_str.c_str()).x))))
);
ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1));
ImGui::Dummy({window_padding, window_padding});
ImGui::SameLine();
imgui.title(cgcode_title_str,true);
ImGui::SameLine(max_len);
imgui.title(cgcode_layer_str, true);
ImGui::SameLine(max_len*1.5);
imgui.title(cgcode_time_str, false);
for (Slic3r::CustomGCode::Item custom_gcode : custom_gcode_per_print_z) {
ImGui::Dummy({window_padding, window_padding});
ImGui::SameLine();
switch (custom_gcode.type) {
case PausePrint: imgui.text(cgcode_pause_str); break;
case Template: imgui.text(cgcode_template_str); break;
case ToolChange: imgui.text(cgcode_toolchange_str); break;
case Custom: imgui.text(cgcode_custom_str); break;
default: imgui.text(cgcode_unknown_str); break;
}
ImGui::SameLine(max_len);
char buf[64];
int layer = m_viewer.get_layer_id_at(m_viewer.get_layer_id_at(custom_gcode.print_z));
::sprintf(buf, "%d",layer );
imgui.text(buf);
ImGui::SameLine(max_len * 1.5);
std::vector<float> layer_times = m_viewer.get_layers_estimated_times();
float custom_gcode_time = 0;
if (layer > 0)
{
for (int i = 0; i < layer-1; i++) {
custom_gcode_time += layer_times[i];
}
}
imgui.text(short_time(get_time_dhms(custom_gcode_time)));
}
}
// total estimated printing time section
ImGui::Spacing();
std::string time_title = m_viewer.get_view_type() == libvgcode::EViewType::FeatureType ? _u8L("Total Estimation") : _u8L("Time Estimation");
auto can_show_mode_button = [this](libvgcode::ETimeMode mode) {
std::vector<std::string> time_strs;
for (size_t i = 0; i < m_print_statistics.modes.size(); ++i) {
if (m_print_statistics.modes[i].time > 0.0f) {
const std::string time_str = short_time(get_time_dhms(m_print_statistics.modes[i].time));
const auto it = std::find(time_strs.begin(), time_strs.end(), time_str);
if (it == time_strs.end())
time_strs.emplace_back(time_str);
}
}
return time_strs.size() > 1;
};
const libvgcode::ETimeMode time_mode_id = m_viewer.get_time_mode();
if (can_show_mode_button(time_mode_id)) {
switch (time_mode_id)
{
case libvgcode::ETimeMode::Normal: { time_title += " [" + _u8L("Normal mode") + "]"; break; }
default: { assert(false); break; }
}
}
ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.title(time_title);
std::string total_filament_str = _u8L("Total Filament");
std::string model_filament_str = _u8L("Model Filament");
std::string cost_str = _u8L("Cost");
std::string prepare_str = _u8L("Prepare time");
std::string print_str = _u8L("Model printing time");
std::string total_str = _u8L("Total time");
float max_len = window_padding + 2 * ImGui::GetStyle().ItemSpacing.x;
if (m_viewer.get_layers_estimated_times().empty())
max_len += ImGui::CalcTextSize(total_str.c_str()).x;
else {
if (m_viewer.get_view_type() == libvgcode::EViewType::FeatureType)
max_len += std::max(ImGui::CalcTextSize(cost_str.c_str()).x,
std::max(ImGui::CalcTextSize(print_str.c_str()).x,
std::max(std::max(ImGui::CalcTextSize(prepare_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x),
std::max(ImGui::CalcTextSize(total_filament_str.c_str()).x, ImGui::CalcTextSize(model_filament_str.c_str()).x))));
else
max_len += std::max(ImGui::CalcTextSize(print_str.c_str()).x,
(std::max(ImGui::CalcTextSize(prepare_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x)));
}
if (m_viewer.get_view_type() == libvgcode::EViewType::FeatureType) {
//BBS display filament cost
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(total_filament_str + ":");
ImGui::SameLine(max_len);
//BBS: use current plater's print statistics
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
char buf[64];
::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", ps.total_used_filament / koef);
imgui.text(buf);
ImGui::SameLine();
::sprintf(buf, imperial_units ? " %.2f oz" : " %.2f g", ps.total_weight / unit_conver);
imgui.text(buf);
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(model_filament_str + ":");
ImGui::SameLine(max_len);
auto exlude_m = total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m;
auto exlude_g = total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g;
::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", ps.total_used_filament / koef - exlude_m);
imgui.text(buf);
ImGui::SameLine();
::sprintf(buf, imperial_units ? " %.2f oz" : " %.2f g", (ps.total_weight - exlude_g) / unit_conver);
imgui.text(buf);
//BBS: display cost of filaments
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(cost_str + ":");
ImGui::SameLine(max_len);
::sprintf(buf, "%.2f", ps.total_cost);
imgui.text(buf);
}
//BBS: start gcode is mostly same with prepeare time
if (time_mode.prepare_time != 0.0f) {
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(prepare_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.prepare_time)));
}
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(print_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.time - time_mode.prepare_time)));
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
imgui.text(total_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.time)));
auto show_mode_button = [this, &imgui, can_show_mode_button](const std::string& label, libvgcode::ETimeMode mode) {
if (can_show_mode_button(mode)) {
if (imgui.button(label)) {
m_viewer.set_time_mode(mode);
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
imgui.set_requires_extra_frame();
#else
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
}
}
};
switch (time_mode_id) {
case libvgcode::ETimeMode::Normal: {
show_mode_button(_u8L("Show stealth mode"), libvgcode::ETimeMode::Stealth);
break;
}
case libvgcode::ETimeMode::Stealth: {
show_mode_button(_u8L("Show normal mode"), libvgcode::ETimeMode::Normal);
break;
}
default : { assert(false); break; }
}
if (m_viewer.get_view_type() == libvgcode::EViewType::ColorPrint) {
ImGui::Spacing();
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
offsets = calculate_offsets({ { _u8L("Options"), { ""}}, { _u8L("Display"), {""}} }, icon_size);
offsets[1] = std::max(predictable_icon_pos, color_print_offsets[_u8L("Display")]); // ORCA prefer predictable_icon_pos when header not reacing end
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
for (auto item : m_viewer.get_options())
append_option_item(item, offsets);
}
legend_height = ImGui::GetCurrentWindow()->Size.y;
ImGui::Dummy({ window_padding, window_padding});
imgui.end();
ImGui::PopStyleColor(6);
ImGui::PopStyleVar(2);
}
void GCodeViewer::push_combo_style()
{
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f * m_scale); // ORCA scale rounding
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * m_scale); // ORCA scale frame size
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0,8.0));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.0f, 0.0f, 0.0f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.59f, 0.53f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.59f, 0.53f, 1.0f));
}
void GCodeViewer::pop_combo_style()
{
ImGui::PopStyleVar(3);
ImGui::PopStyleColor(8);
}
void GCodeViewer::render_slider(int canvas_width, int canvas_height) {
m_moves_slider->render(canvas_width, canvas_height);
m_layers_slider->render(canvas_width, canvas_height);
}
} // namespace GUI
} // namespace Slic3r