mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-16 23:42:11 +00:00
* feat(viewer): Display travel distance and move count in G-code summary This commit introduces a new feature that enhances the G-code viewer by displaying the total travel distance and the total number of travel moves in the 'Line Type' summary. This provides users with more detailed statistics about their prints, helping them to better understand the printer's behavior and identify opportunities to optimize travel moves for faster print times. This commit also fixes a critical bug in the G-code processor where the travel distance was being calculated incorrectly. The distance variable was not being updated for non-extruding travel moves, leading to inaccurate statistics. The calculation has been corrected to ensure it is performed for all relevant move types, resulting in accurate travel distance reporting. * Subfix segments kilo mega giga tera peta exa * Add missing values * Grams to Kilos and tons * add distance * Fix tool view * Record and display seam distances Track seam-related distances in print statistics and show them in the GCode viewer. Added total_seam_gap_distance and total_seam_scarf_distance to PrintEstimatedStatistics (with initialization). In GCode::extrude_loop the code now computes seam gap and scarf distances and accumulates them for external perimeters. GCodeViewer uses the summed seam distance when the Seams option is selected in the legend. * Fix travel / wipe distances * Update GCode.cpp * Filament changes estimated time --------- Co-authored-by: Steve Scargall <37674041+sscargal@users.noreply.github.com>
4718 lines
235 KiB
C++
4718 lines
235 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 "FilamentGroupPopup.hpp"
|
|
#include "GLToolbar.hpp"
|
|
#include "GUI_Preview.hpp"
|
|
#include "libslic3r/Print.hpp"
|
|
#include "libslic3r/Layer.hpp"
|
|
#include "Widgets/ProgressDialog.hpp"
|
|
#include "MsgDialog.hpp"
|
|
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
|
|
#include <imgui/imgui_internal.h>
|
|
|
|
#include <glad/gl.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 <wx/utils.h>
|
|
|
|
#include <array>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#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::Summary)
|
|
return _u8L("Summary");
|
|
else 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::Acceleration)
|
|
return _u8L("Acceleration");
|
|
else if (view_type == libvgcode::EViewType::Jerk)
|
|
return _u8L("Jerk");
|
|
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)");
|
|
// ORCA: Add Pressure Advance visualization support
|
|
else if (view_type == libvgcode::EViewType::PressureAdvance)
|
|
return _u8L("Pressure Advance");
|
|
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;
|
|
}
|
|
|
|
static std::string format_compact_weight(double value_in_grams, bool imperial_units)
|
|
{
|
|
char buffer[64];
|
|
if (imperial_units) {
|
|
::sprintf(buffer, "%.2f oz", value_in_grams / GizmoObjectManipulation::oz_to_g);
|
|
return buffer;
|
|
}
|
|
|
|
const double abs_value = value_in_grams < 0.0 ? -value_in_grams : value_in_grams;
|
|
const char* unit = "g";
|
|
double scaled_value = abs_value;
|
|
if (scaled_value >= 1000000.0) {
|
|
scaled_value /= 1000000.0;
|
|
unit = "t";
|
|
} else if (scaled_value >= 1000.0) {
|
|
scaled_value /= 1000.0;
|
|
unit = "kg";
|
|
}
|
|
|
|
::sprintf(buffer, "%s%.2f%s", value_in_grams < 0.0 ? "-" : "", scaled_value, unit);
|
|
return buffer;
|
|
}
|
|
|
|
#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, 150.f / 255.0f, 136.0f / 255.f, 0.5f)); // ORCA color with opacity
|
|
|
|
// 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(ImVec4(0.0f, 150.f / 255.0f, 136.0f / 255.f, 1.0f)); // ORCA color
|
|
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
|
|
|
|
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 });
|
|
}
|
|
|
|
//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;
|
|
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 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 G-code"); }
|
|
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)
|
|
{ // ORCA matched terms
|
|
case libvgcode::EGCodeExtrusionRole::None: { return _u8L("Unknown"); }
|
|
case libvgcode::EGCodeExtrusionRole::Perimeter: { return _u8L("Inner wall"); }
|
|
case libvgcode::EGCodeExtrusionRole::ExternalPerimeter: { return _u8L("Outer wall"); }
|
|
case libvgcode::EGCodeExtrusionRole::OverhangPerimeter: { return _u8L("Overhang wall"); }
|
|
case libvgcode::EGCodeExtrusionRole::InternalInfill: { return _u8L("Sparse infill"); }
|
|
case libvgcode::EGCodeExtrusionRole::SolidInfill: { return _u8L("Internal solid infill"); }
|
|
case libvgcode::EGCodeExtrusionRole::TopSolidInfill: { return _u8L("Top surface"); }
|
|
case libvgcode::EGCodeExtrusionRole::Ironing: { return _u8L("Ironing"); }
|
|
case libvgcode::EGCodeExtrusionRole::BridgeInfill: { return _u8L("Bridge"); }
|
|
case libvgcode::EGCodeExtrusionRole::GapFill: { return _u8L("Gap infill"); }
|
|
case libvgcode::EGCodeExtrusionRole::Skirt: { return _u8L("Skirt"); }
|
|
case libvgcode::EGCodeExtrusionRole::SupportMaterial: { return _u8L("Support"); }
|
|
case libvgcode::EGCodeExtrusionRole::SupportMaterialInterface: { return _u8L("Support interface"); }
|
|
case libvgcode::EGCodeExtrusionRole::WipeTower: { return _u8L("Prime tower"); }
|
|
case libvgcode::EGCodeExtrusionRole::Custom: { return _u8L("Custom"); }
|
|
// ORCA
|
|
case libvgcode::EGCodeExtrusionRole::BottomSurface: { return _u8L("Bottom surface"); }
|
|
case libvgcode::EGCodeExtrusionRole::InternalBridgeInfill: { return _u8L("Internal bridge"); } // ORCA
|
|
case libvgcode::EGCodeExtrusionRole::Brim: { return _u8L("Brim"); }
|
|
case libvgcode::EGCodeExtrusionRole::SupportTransition: { return _u8L("Support transition"); }
|
|
case libvgcode::EGCodeExtrusionRole::Mixed: { return _u8L("Mixed"); }
|
|
default: { return _u8L("Unknown"); }
|
|
}
|
|
}
|
|
|
|
void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode::Viewer* viewer, int canvas_width, int canvas_height, const libvgcode::EViewType& view_type)
|
|
{
|
|
static bool properties_shown = false;
|
|
|
|
const std::string NA_TXT = _u8L("N/A");
|
|
const char* NA_CSTR = NA_TXT.c_str();
|
|
|
|
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>(canvas_width), static_cast<float>(canvas_height), ImGuiCond_Always, 0.5f, 1.0f);
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGuiWrapper::COL_BUTTON_BACKGROUND);
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGuiWrapper::COL_BUTTON_ACTIVE);
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGuiWrapper::COL_BUTTON_HOVERED);
|
|
ImGui::PushStyleColor(ImGuiCol_Border , ImVec4(.0f,.0f,.0f,.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.f,1.f,1.f,.6f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f * m_scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding , ImVec2(10.f, 10.f) * m_scale);
|
|
ImGui::SetNextWindowBgAlpha(0.8f);
|
|
|
|
libvgcode::PathVertex vertex = viewer->get_current_vertex();
|
|
size_t vertex_id = viewer->get_current_vertex_id();
|
|
// Seam moves may contain non-deterministic vertex data, so use the
|
|
// last visible non-seam vertex instead (except in FeatureType view)
|
|
if (view_type != libvgcode::EViewType::FeatureType && vertex.type == libvgcode::EMoveType::Seam) {
|
|
const libvgcode::Interval& visible_range = viewer->get_view_visible_range();
|
|
if (visible_range[1] > 0) {
|
|
vertex_id = static_cast<size_t>(visible_range[1]) - 1;
|
|
vertex = viewer->get_vertex_at(vertex_id);
|
|
}
|
|
}
|
|
const bool is_extrusion = vertex.is_extrusion();
|
|
|
|
const ImGuiStyle& style = ImGui::GetStyle();
|
|
|
|
char detail_buf[1024];
|
|
switch (view_type) {
|
|
case libvgcode::EViewType::FeatureType:
|
|
if (is_extrusion)
|
|
strcpy(detail_buf, to_string(vertex.role).c_str());
|
|
else if (vertex.type != libvgcode::EMoveType::Noop)
|
|
strcpy(detail_buf, to_string(vertex.type).c_str());
|
|
else
|
|
strcpy(detail_buf, NA_CSTR); // Noop moves are not shown in the "Line type" view, so show N/A for them.
|
|
break;
|
|
case libvgcode::EViewType::Height:
|
|
if (is_extrusion)
|
|
sprintf(detail_buf, "%s%.2f", _u8L("Height: ").c_str(), vertex.height);
|
|
else
|
|
sprintf(detail_buf, "%s%s", _u8L("Height: ").c_str(), NA_CSTR);
|
|
break;
|
|
case libvgcode::EViewType::Width:
|
|
if (is_extrusion)
|
|
sprintf(detail_buf, "%s%.2f", _u8L("Width: ").c_str(), vertex.width);
|
|
else
|
|
sprintf(detail_buf, "%s%s", _u8L("Width: ").c_str(), NA_CSTR);
|
|
break;
|
|
case libvgcode::EViewType::VolumetricFlowRate:
|
|
if (is_extrusion)
|
|
sprintf(detail_buf, "%s%.2f", _u8L("Flow: ").c_str(), vertex.volumetric_rate());
|
|
else
|
|
sprintf(detail_buf, "%s%s", _u8L("Flow: ").c_str(), NA_CSTR);
|
|
break;
|
|
case libvgcode::EViewType::FanSpeed:
|
|
sprintf(detail_buf, "%s%.0f", _u8L("Fan: ").c_str(), vertex.fan_speed);
|
|
break;
|
|
case libvgcode::EViewType::Temperature:
|
|
sprintf(detail_buf, "%s%.0f", _u8L("Temperature: ").c_str(), vertex.temperature);
|
|
break;
|
|
case libvgcode::EViewType::LayerTimeLinear:
|
|
case libvgcode::EViewType::LayerTimeLogarithmic:
|
|
sprintf(detail_buf, "%s%.1f", _u8L("Layer Time: ").c_str(), vertex.layer_duration);
|
|
break;
|
|
case libvgcode::EViewType::Tool:
|
|
sprintf(detail_buf, "%s%d", _u8L("Tool: ").c_str(), vertex.extruder_id + 1);
|
|
break;
|
|
case libvgcode::EViewType::ColorPrint:
|
|
sprintf(detail_buf, "%s%d", _u8L("Color: ").c_str(), vertex.color_id + 1);
|
|
break;
|
|
case libvgcode::EViewType::Acceleration:
|
|
sprintf(detail_buf, "%s%.0f", _u8L("Acceleration: ").c_str(), vertex.acceleration);
|
|
break;
|
|
case libvgcode::EViewType::Jerk:
|
|
sprintf(detail_buf, "%s%.1f", _u8L("Jerk: ").c_str(), vertex.jerk);
|
|
break;
|
|
case libvgcode::EViewType::PressureAdvance:
|
|
sprintf(detail_buf, "%s%.4f", _u8L("PA: ").c_str(), vertex.pressure_advance);
|
|
break;
|
|
default:
|
|
detail_buf[0] = '\0';
|
|
break;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> properties_rows;
|
|
float table_content_w = 0.0f;
|
|
if (properties_shown) {
|
|
float label_w = 0.0f;
|
|
float value_w = 0.0f;
|
|
properties_rows.reserve(13);
|
|
auto add_row = [&properties_rows, &label_w, &value_w](std::string label, std::string value) {
|
|
label_w = std::max(label_w, ImGui::CalcTextSize(label.c_str()).x);
|
|
value_w = std::max(value_w, ImGui::CalcTextSize(value.c_str()).x);
|
|
properties_rows.emplace_back(std::move(label), std::move(value));
|
|
};
|
|
char buff[1024];
|
|
add_row(_u8L("Type"), _u8L(to_string(vertex.type)));
|
|
add_row(_u8L("Line Type"), is_extrusion ? _u8L(to_string(vertex.role)) : NA_TXT);
|
|
if (is_extrusion) sprintf(buff, ("%.3f " + _u8L("mm")).c_str(), vertex.width); else strcpy(buff, NA_CSTR);
|
|
add_row(_u8L("Width"), buff);
|
|
if (is_extrusion) sprintf(buff, ("%.3f " + _u8L("mm")).c_str(), vertex.height); else strcpy(buff, NA_CSTR);
|
|
add_row(_u8L("Height"), buff);
|
|
sprintf(buff, "%d", vertex.layer_id + 1);
|
|
add_row(_u8L("Layer"), buff);
|
|
sprintf(buff, ("%.1f " + _u8L("mm/s")).c_str(), vertex.feedrate);
|
|
add_row(_u8L("Speed"), buff);
|
|
sprintf(buff, ("%.0f " + _u8L("mm/s²")).c_str(), vertex.acceleration);
|
|
add_row(_u8L("Acceleration"), buff);
|
|
sprintf(buff, ("%.1f " + _u8L("mm/s")).c_str(), vertex.jerk);
|
|
add_row(_u8L("Jerk"), buff);
|
|
if (is_extrusion) sprintf(buff, ("%.3f " + _u8L("mm³/s")).c_str(), vertex.volumetric_rate()); else strcpy(buff, NA_CSTR);
|
|
add_row(_u8L("Flow rate"), buff);
|
|
sprintf(buff, "%.0f %%", vertex.fan_speed);
|
|
add_row(_u8L("Fan speed"), buff);
|
|
sprintf(buff, ("%.0f " + _u8L("°C")).c_str(), vertex.temperature);
|
|
add_row(_u8L("Temperature"), buff);
|
|
sprintf(buff, "%.4f", vertex.pressure_advance);
|
|
add_row(_u8L("Pressure Advance"), buff);
|
|
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())]);
|
|
add_row(_u8L("Time"), buff);
|
|
|
|
table_content_w = 2 * std::max(label_w, value_w) + style.ItemInnerSpacing.x;
|
|
}
|
|
|
|
// "ToolPosition" layout width calculation
|
|
const float axes_spacing_x = ImGui::CalcTextSize("sp").x; // Spacing between axes labels
|
|
const float axes_width = ImGui::CalcTextSize("X 999.999").x; // Width of the longest possible axis label
|
|
const float axes_content_w = 3.0f * axes_width + 2.0f * axes_spacing_x; // Three axes
|
|
const float speed_width = ImGui::CalcTextSize((_u8L("Speed: ") + "9999 ").c_str()).x; // Width of the longest possible speed label
|
|
const float detail_width = ImGui::CalcTextSize(detail_buf).x; // Width of the detail text
|
|
const float info_content_w = speed_width + detail_width; // Speed + detail
|
|
const float info_group_w = std::max(axes_content_w, info_content_w); // The width of the group containing position and detail info, whichever is wider
|
|
const float fold_button_w = 24.0f * m_scale; // Width of the fold/unfold button
|
|
const float bottom_row_w = fold_button_w + style.ItemSpacing.x + info_group_w; // The width of the bottom row containing the fold button and the info group
|
|
float speed_profile_row_w = 0.0f;
|
|
#if ENABLE_ACTUAL_SPEED_DEBUG
|
|
const float table_btn_width = std::max(ImGui::CalcTextSize(_u8L("Hide").c_str()).x, ImGui::CalcTextSize(_u8L("Show").c_str()).x);
|
|
const float btn_padding_x = 8.0f * m_scale;
|
|
|
|
if (properties_shown) {
|
|
// The width of the row containing the button to show/hide the actual speed profile table and its label
|
|
speed_profile_row_w = table_btn_width + btn_padding_x * 2.0f + style.ItemSpacing.x + ImGui::CalcTextSize(_u8L("Actual speed profile").c_str()).x;
|
|
}
|
|
#endif // ENABLE_ACTUAL_SPEED_DEBUG
|
|
const float properties_w = std::max(table_content_w, speed_profile_row_w); // The width of the properties section, whichever is wider between the properties table and the speed profile row
|
|
const float content_w = std::max(bottom_row_w, properties_w); // The width of the window content, whichever is wider between the bottom row and the properties table
|
|
const float window_w = std::ceil(content_w + 2.0f * style.WindowPadding.x); // Final window width with padding, rounded up for better look
|
|
|
|
// "ToolPosition" layout height calculation
|
|
const float text_h = ImGui::GetTextLineHeight();
|
|
const float item_spacing_y = style.ItemSpacing.y;
|
|
const float cell_pad_y = style.CellPadding.y;
|
|
const float window_pad_h = 2.0f * style.WindowPadding.y;
|
|
const float show_button_h = text_h + 2.0f * 3.0f * m_scale; // ImGuiStyleVar_FramePadding.y is set to 3.f * m_scale
|
|
const float main_row_h = 2.0f * text_h + item_spacing_y; // Two lines of text (position and detail) + spacing between them
|
|
const float properties_h = static_cast<float>(properties_rows.size()) * (text_h + 2.0f * cell_pad_y) + 2.0f * cell_pad_y + 1.0f + item_spacing_y // table rows
|
|
+ item_spacing_y + show_button_h // Spacing() + Show/Hide button row
|
|
+ item_spacing_y + 1.0f + style.FramePadding.y; // Spacing() + Separator() + Dummy()
|
|
const float folded_window_h = std::ceil(window_pad_h + main_row_h); // Height of the window when properties are hidden, with padding, rounded up for better look
|
|
const float unfolded_window_h = std::ceil(folded_window_h + properties_h); // Height of the window when properties are shown, with padding, rounded up for better look
|
|
const float window_h = properties_shown ? unfolded_window_h : folded_window_h; // Final window height depending on whether properties are shown or not
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(window_w, window_h), ImGuiCond_Always);
|
|
|
|
imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
|
|
ImGui::AlignTextToFramePadding();
|
|
|
|
if (properties_shown) {
|
|
auto append_table_row = [](const std::string& label, const std::string& value) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORCA, label);
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGuiWrapper::text(value);
|
|
};
|
|
|
|
if (ImGui::BeginTable("Properties", 2)) {
|
|
for (const auto& row : properties_rows)
|
|
append_table_row(row.first, row.second);
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
#if ENABLE_ACTUAL_SPEED_DEBUG
|
|
bool actual_speed_exist = vertex.is_extrusion() || vertex.is_travel() || vertex.is_wipe();
|
|
static float cached_table_wnd_width = 0.0f; // ORCA: Cache the calculated window width to avoid recalculation on every frame
|
|
static float cached_table_wnd_scale = 0.0f; // ORCA: Cache the calculated window scale to avoid recalculation on every frame
|
|
ImGui::Spacing();
|
|
|
|
static bool table_shown = false;
|
|
imgui.push_confirm_button_style();
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding , 3.f * m_scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding , ImVec2(btn_padding_x, 3.f * m_scale));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(.5f, .5f));
|
|
if (imgui.button((table_shown ? _L("Hide") : _L("Show")).c_str(), ImVec2(table_btn_width + btn_padding_x * 2.f, 0.f), actual_speed_exist))
|
|
table_shown = !table_shown;
|
|
ImGui::PopStyleVar(3);
|
|
imgui.pop_confirm_button_style();
|
|
ImGui::SameLine();
|
|
ImGuiWrapper::text(_u8L("Actual speed profile").c_str()); // ORCA show label and plot on external window to make main window more compact
|
|
|
|
if (actual_speed_exist && table_shown) {
|
|
const float plot_height = 135.f * m_scale; // 135 is the height of the plot without labels
|
|
|
|
if (cached_table_wnd_scale != m_scale) { // ORCA
|
|
// Catch the window width to avoid recalculation on every frame,
|
|
// but recalculate it when/if scale changes.
|
|
|
|
const float cell_pad_x = 9.f * m_scale; // matches ImGuiStyleVar_CellPadding.x below
|
|
const float wnd_pad_x = 2.0f * style.WindowPadding.x;
|
|
const float table_border_x = 6.0f * m_scale;
|
|
|
|
// Calculate the width needed to fully show each table column header
|
|
// but not clamping to a minimum because later the window width is clamped anyway.
|
|
const float col0_w = ImGui::CalcTextSize((_u8L("Position") + " (" + _u8L("mm") + ")").c_str()).x;
|
|
const float col1_w = ImGui::CalcTextSize((_u8L("Speed") + " (" + _u8L("mm/s") + ")").c_str()).x;
|
|
|
|
const float content_w = std::ceil(col0_w + col1_w + 4.0f * cell_pad_x + table_border_x + wnd_pad_x);
|
|
const float aspect_w = std::ceil(plot_height * (16.0f / 9.0f)); // 16:9 ratio for better look
|
|
// Try to obtain 16:9 ratio if possible, but keep enough width for content to make
|
|
// window looking good on different languages, especially with long words in table header
|
|
cached_table_wnd_width = std::max(content_w, aspect_w);
|
|
cached_table_wnd_scale = m_scale;
|
|
}
|
|
|
|
// ORCA: Pre-calculate the height of the table based on number of rows
|
|
// and clamp the final window height to keep at least the same height as ToolPosition window.
|
|
const float cell_pad_y = 1.f * m_scale; // ImGuiStyleVar_CellPadding.y below
|
|
const float row_height = ImGui::GetTextLineHeight() + 2.f * cell_pad_y;
|
|
const float rows = static_cast<float>(m_actual_speed_imgui_widget.data.size());
|
|
const float table_h = row_height * (1.0f + rows) + 2.f * m_scale; // border allowance
|
|
const float target_h = std::ceil(plot_height + table_h + 2.f * style.WindowPadding.y);
|
|
const float min_h = ImGui::GetWindowHeight(); // keep at least the same height as ToolPosition window
|
|
const float max_h = static_cast<float>(canvas_height) - 8.f * m_scale; // keep inside canvas with a small margin
|
|
const float final_h = std::clamp(target_h, min_h, max_h);
|
|
const bool needs_scroll = target_h > max_h;
|
|
const float table_view_h = std::max(1.0f, final_h - plot_height - 2.f * style.WindowPadding.y);
|
|
|
|
//const ImVec2 wnd_size = ImGui::GetWindowSize();
|
|
imgui.set_next_window_pos(ImGui::GetWindowPos().x - 5.f * m_scale /*+ wnd_size.x*/, static_cast<float>(canvas_height), ImGuiCond_Always, 1.0f, 1.0f);
|
|
ImGui::SetNextWindowSize(ImVec2(cached_table_wnd_width, final_h), ImGuiCond_Always);
|
|
//ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, wnd_size.y });
|
|
imgui.begin(std::string("ToolPositionTableWnd"), ImGuiWindowFlags_NoTitleBar |
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(9.f, 1.f) * m_scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, 0.f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered , style.Colors[ImGuiCol_TableHeaderBg]);
|
|
const int hover_id = m_actual_speed_imgui_widget.plot("##ActualSpeedProfile", { -1.f, plot_height});
|
|
const ImGuiTableFlags table_flags = ImGuiTableFlags_Borders | (needs_scroll ? ImGuiTableFlags_ScrollY : 0);
|
|
if (ImGui::BeginTable("ToolPositionTable", 2, table_flags, ImVec2(0.0f, needs_scroll ? table_view_h : 0.0f))) {
|
|
char buff[1024];
|
|
if (needs_scroll)
|
|
ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
|
|
ImGui::TableSetupColumn((_u8L("Position") + " (" + _u8L("mm") + ")").c_str());
|
|
ImGui::TableSetupColumn((_u8L("Speed") + " (" + _u8L("mm/s") + ")").c_str());
|
|
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);
|
|
ImGui::TableNextRow();
|
|
const ImU32 row_bg_color = ImGui::GetColorU32(item.internal ? ImVec4(0.0f, 150.f / 255.0f, 136.0f / 255.f, 0.15f) : ImVec4(0.2f, 0.2f, 0.2f, 0.25f)); // ORCA
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, row_bg_color);
|
|
ImGui::TableSetColumnIndex(0);
|
|
sprintf(buff, "%.3f", item.pos);
|
|
imgui.text_colored(highlight ? ImGuiWrapper::COL_ORCA : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), buff);
|
|
ImGui::TableSetColumnIndex(1);
|
|
sprintf(buff, "%.1f", item.speed);
|
|
imgui.text_colored(highlight ? ImGuiWrapper::COL_ORCA : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), buff);
|
|
++counter;
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(2);
|
|
ImGui::PopStyleColor(1);
|
|
imgui.end();
|
|
}
|
|
|
|
#endif // ENABLE_ACTUAL_SPEED_DEBUG
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Dummy({0, style.FramePadding.y});
|
|
}
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding , 3.f * m_scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding , ImVec2(2.f, 2.f) * m_scale);
|
|
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));
|
|
|
|
const float main_wnd_height = ImGui::GetWindowHeight();
|
|
// ORCA use glyph based button for fixing button sizes changing depends on used font size on platform
|
|
const wchar_t foldIcon = properties_shown ? ImGui::UnfoldButtonIcon : ImGui::FoldButtonIcon;
|
|
if (imgui.glyph_button(foldIcon, ImVec2(16.f, 16.f) * m_scale)) {
|
|
properties_shown = !properties_shown;
|
|
static float main_wnd_height_temp = ImGui::GetWindowHeight();
|
|
static float first_click = true;
|
|
if (first_click || main_wnd_height_temp != main_wnd_height) {
|
|
main_wnd_height_temp = main_wnd_height;
|
|
first_click = false;
|
|
imgui.set_requires_extra_frame();
|
|
}
|
|
}
|
|
ImGui::PopStyleColor(3);
|
|
ImGui::PopStyleVar(2);
|
|
|
|
ImGui::SameLine();
|
|
|
|
if(!properties_shown)
|
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - style.FramePadding.y); // aligns button with next group
|
|
|
|
ImGui::BeginGroup(); // group contents to make information area more compact
|
|
|
|
// ORCA Use colorized axes labels & reduce precision on big scaled prints
|
|
auto pos =vertex.position;
|
|
int max_value = std::round(std::max(std::max(pos[0], pos[1]),pos[2]));
|
|
auto precision = max_value > 9999 ? "%.1f" : max_value > 999 ? "%.2f" : "%.3f";
|
|
|
|
char xBuf[32];
|
|
ImGui::TextColored(ImGuiWrapper::to_ImVec4(ColorRGBA::X()),"X ");
|
|
ImGui::SameLine(0,0); // ignore item spacing
|
|
sprintf(xBuf, precision, pos[0]);
|
|
ImGui::Text("%s", xBuf);
|
|
|
|
char yBuf[32];
|
|
ImGui::SameLine(axes_width + axes_spacing_x);
|
|
ImGui::TextColored(ImGuiWrapper::to_ImVec4(ColorRGBA::Y()),"Y ");
|
|
ImGui::SameLine(0,0); // ignore item spacing
|
|
sprintf(yBuf, precision, pos[1]);
|
|
ImGui::Text("%s", yBuf);
|
|
|
|
char zBuf[32];
|
|
ImGui::SameLine(2.0f * (axes_width + axes_spacing_x));
|
|
ImGui::TextColored(ImGuiWrapper::to_ImVec4(ColorRGBA::Z()),"Z ");
|
|
ImGui::SameLine(0,0); // ignore item spacing
|
|
sprintf(zBuf, precision, pos[2]);
|
|
ImGui::Text("%s", zBuf);
|
|
|
|
ImGui::SameLine(3.0f * axes_width + 2.0f * axes_spacing_x);
|
|
ImGui::Dummy({0,0});
|
|
|
|
char spdBuf[128];
|
|
sprintf(spdBuf, "%s%.0f ", _u8L("Speed: ").c_str(), vertex.feedrate);
|
|
ImGuiWrapper::text(spdBuf); // render Speed as differrent item to keep next item in same place
|
|
|
|
if (detail_buf[0] != '\0') { // dont render if buffer empty
|
|
ImGui::SameLine(speed_width);
|
|
ImGuiWrapper::text(detail_buf);
|
|
}
|
|
ImGui::EndGroup();
|
|
|
|
imgui.end();
|
|
ImGui::PopStyleColor(5);
|
|
ImGui::PopStyleVar(2);
|
|
}
|
|
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.set_next_window_pos(0.5f * static_cast<float>(canvas_width), static_cast<float>(canvas_height), ImGuiCond_Always, 0.5f, 1.0f);
|
|
ImGui::PushStyleColor(ImGuiCol_Border , ImVec4(.0f,.0f,.0f,.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.f,1.f,1.f,.6f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f * m_scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding , ImVec2(10.f, 10.f) * m_scale);
|
|
ImGui::SetNextWindowBgAlpha(0.8f);
|
|
imgui.begin(std::string("ToolPosition"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
|
|
|
|
const Vec3f pos = m_world_position + m_world_offset + m_z_offset * Vec3f::UnitZ();
|
|
|
|
int max_value = std::round(std::max(std::max(pos.x(), pos.y()),pos.z()));
|
|
auto precision = max_value > 9999 ? "%.1f" : max_value > 999 ? "%.2f" : "%.3f";
|
|
const float axes_width = ImGui::CalcTextSize("X 999.999sp").x;
|
|
|
|
char xBuf[32];
|
|
ImGui::TextColored(ImGuiWrapper::to_ImVec4(ColorRGBA::X()),"X ");
|
|
ImGui::SameLine(0,0); // ignore item spacing
|
|
sprintf(xBuf, precision, pos.x());
|
|
ImGui::Text("%s", xBuf);
|
|
|
|
char yBuf[32];
|
|
ImGui::SameLine(axes_width);
|
|
ImGui::TextColored(ImGuiWrapper::to_ImVec4(ColorRGBA::Y()),"Y ");
|
|
ImGui::SameLine(0,0); // ignore item spacing
|
|
sprintf(yBuf, precision, pos.y());
|
|
ImGui::Text("%s", yBuf);
|
|
|
|
char zBuf[32];
|
|
ImGui::SameLine(axes_width * 2);
|
|
ImGui::TextColored(ImGuiWrapper::to_ImVec4(ColorRGBA::Z()),"Z ");
|
|
ImGui::SameLine(0,0); // ignore item spacing
|
|
sprintf(zBuf, precision, pos.z());
|
|
ImGui::Text("%s", zBuf);
|
|
|
|
ImGui::SameLine(axes_width * 3);
|
|
ImGui::Dummy({0,0});
|
|
|
|
imgui.end();
|
|
ImGui::PopStyleColor(2);
|
|
ImGui::PopStyleVar(2);
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
// 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;
|
|
|
|
float required_width = 0.0f;
|
|
{ // --- Compute required G-code window width ---
|
|
float max_text_width = id_width;
|
|
for (const Line& line : m_lines) {
|
|
float w = 0.0f;
|
|
|
|
if (!line.command.empty())
|
|
w += ImGui::CalcTextSize(line.command.c_str()).x;
|
|
|
|
if (!line.parameters.empty())
|
|
w += ImGui::CalcTextSize(line.parameters.c_str()).x;
|
|
|
|
if (!line.comment.empty())
|
|
w += ImGui::CalcTextSize(line.comment.c_str()).x;
|
|
|
|
// spacing between segments
|
|
w += 3 * ImGui::GetStyle().ItemSpacing.x;
|
|
|
|
if (w > max_text_width)
|
|
max_text_width = w;
|
|
}
|
|
|
|
required_width = id_width + max_text_width;
|
|
}
|
|
|
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
|
|
|
//BBS: GUI refactor: move to right
|
|
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::SetNextWindowSize(ImVec2(required_width, 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_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_offset(current_offset);
|
|
marker.render(canvas_width, canvas_height, view_type);
|
|
marker.render_position_window(viewer, 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);
|
|
}
|
|
|
|
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;
|
|
|
|
// 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();
|
|
}
|
|
|
|
// Orca:
|
|
// Default view type at first slice.
|
|
// May be overridden in load() once we know how many tools are actually used in the G-code.
|
|
m_nozzle_nums = preset_bundle ? preset_bundle->get_printer_extruder_count() : 1;
|
|
auto it = std::find(view_type_items.begin(), view_type_items.end(), libvgcode::EViewType::FeatureType);
|
|
m_view_type_sel = (it != view_type_items.end()) ? std::distance(view_type_items.begin(), it) : 0;
|
|
set_view_type(libvgcode::EViewType::FeatureType);
|
|
|
|
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::Summary);
|
|
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::ActualSpeed);
|
|
view_type_items.push_back(libvgcode::EViewType::Acceleration);
|
|
view_type_items.push_back(libvgcode::EViewType::Jerk);
|
|
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::ActualVolumetricFlowRate);
|
|
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);
|
|
// ORCA: Add Pressure Advance visualization support
|
|
view_type_items.push_back(libvgcode::EViewType::PressureAdvance);
|
|
//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;
|
|
const std::vector<std::string>& str_colors = str_color_print_colors.empty() ? str_tool_colors : str_color_print_colors;
|
|
color_print_colors.reserve(str_colors.size());
|
|
for (const std::string& color : str_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
|
|
wxGetApp().plater()->suppress_background_process(true);
|
|
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();
|
|
wxGetApp().plater()->schedule_background_process();
|
|
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,
|
|
// ORCA
|
|
libvgcode::EGCodeExtrusionRole::BottomSurface, libvgcode::EGCodeExtrusionRole::InternalBridgeInfill, libvgcode::EGCodeExtrusionRole::Brim,
|
|
libvgcode::EGCodeExtrusionRole::SupportTransition, libvgcode::EGCodeExtrusionRole::Mixed
|
|
});
|
|
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.filaments_count;
|
|
|
|
//BBS: move the id to the end of reset
|
|
m_last_result_id = gcode_result.id;
|
|
m_gcode_result = &gcode_result;
|
|
m_move_type_counts.fill(0);
|
|
for (auto& move_type_times : m_move_type_times)
|
|
move_type_times.fill(0.0f);
|
|
m_move_type_distances.fill(0.0f);
|
|
for (const GCodeProcessorResult::MoveVertex& move : gcode_result.moves) {
|
|
if (move.internal_only)
|
|
continue;
|
|
|
|
const size_t move_type = static_cast<size_t>(move.type);
|
|
if (move_type < m_move_type_counts.size()) {
|
|
++m_move_type_counts[move_type];
|
|
for (size_t mode = 0; mode < move.time.size(); ++mode)
|
|
m_move_type_times[move_type][mode] += move.time[mode];
|
|
if (move.type == EMoveType::Retract || move.type == EMoveType::Unretract)
|
|
m_move_type_distances[move_type] += std::fabs(move.delta_extruder);
|
|
else
|
|
m_move_type_distances[move_type] += move.travel_dist;
|
|
}
|
|
}
|
|
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;
|
|
m_z_offset = gcode_result.z_offset;
|
|
|
|
// load_toolpaths(gcode_result, build_volume, exclude_bounding_box);
|
|
|
|
// ORCA: Only show filament/color print preview if more than one tool/extruder is actually used in the toolpaths.
|
|
// Only reset back to Toolpaths (FeatureType) if we are currently in ColorPrint and this load is single-tool.
|
|
if (m_viewer.get_used_extruders_count() > 1) {
|
|
auto it = std::find(view_type_items.begin(), view_type_items.end(), libvgcode::EViewType::ColorPrint);
|
|
if (it != view_type_items.end())
|
|
m_view_type_sel = std::distance(view_type_items.begin(), it);
|
|
set_view_type(libvgcode::EViewType::ColorPrint);
|
|
} else if (get_view_type() == libvgcode::EViewType::ColorPrint) {
|
|
auto it = std::find(view_type_items.begin(), view_type_items.end(), libvgcode::EViewType::FeatureType);
|
|
if (it != view_type_items.end())
|
|
m_view_type_sel = std::distance(view_type_items.begin(), it);
|
|
set_view_type(libvgcode::EViewType::FeatureType);
|
|
}
|
|
|
|
// BBS: data for rendering color arrangement recommendation
|
|
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
|
// Orca hack: Hide filament group for non-bbl printers
|
|
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
|
std::vector<int> filament_maps = print.get_filament_maps();
|
|
std::vector<std::string> color_opt = print.config().option<ConfigOptionStrings>("filament_colour")->values;
|
|
std::vector<std::string> type_opt = print.config().option<ConfigOptionStrings>("filament_type")->values;
|
|
std::vector<unsigned char> support_filament_opt = print.config().option<ConfigOptionBools>("filament_is_support")->values;
|
|
for (auto extruder_id : m_viewer.get_used_extruders_ids()) {
|
|
if (filament_maps[extruder_id] == 1) {
|
|
m_left_extruder_filament.push_back({type_opt[extruder_id], color_opt[extruder_id], extruder_id, (bool)(support_filament_opt[extruder_id])});
|
|
} else {
|
|
m_right_extruder_filament.push_back({type_opt[extruder_id], color_opt[extruder_id], extruder_id, (bool)(support_filament_opt[extruder_id])});
|
|
}
|
|
}
|
|
|
|
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();
|
|
Pointfs wrapping_exclude_area = Pointfs();
|
|
std::vector<Pointfs> extruder_areas;
|
|
std::vector<double> extruder_heights;
|
|
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;
|
|
|
|
if (!gcode_result.wrapping_exclude_area.empty())
|
|
wrapping_exclude_area = gcode_result.wrapping_exclude_area;
|
|
|
|
if (!gcode_result.extruder_areas.empty())
|
|
extruder_areas = gcode_result.extruder_areas;
|
|
if (!gcode_result.extruder_heights.empty())
|
|
extruder_heights = gcode_result.extruder_heights;
|
|
|
|
wxGetApp().plater()->set_bed_shape(printable_area, bed_exclude_area, wrapping_exclude_area, gcode_result.printable_height, extruder_areas, extruder_heights, 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
|
|
if (m_viewer.get_used_extruders_count() > 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();
|
|
|
|
//BBS
|
|
m_conflict_result = gcode_result.conflict_result;
|
|
if (m_conflict_result) { m_conflict_result.value().layer = m_viewer.get_layer_id_at(static_cast<float>(m_conflict_result.value()._height)); }
|
|
|
|
m_gcode_check_result = gcode_result.gcode_check_result;
|
|
|
|
filament_printable_reuslt = gcode_result.filament_printable_reuslt;
|
|
//BBS: add mutex for protection of gcode result
|
|
gcode_result.unlock();
|
|
wxGetApp().plater()->schedule_background_process();
|
|
}
|
|
|
|
void GCodeViewer::load_as_preview(libvgcode::GCodeInputData&& data)
|
|
{
|
|
m_loaded_as_preview = true;
|
|
|
|
m_move_type_counts.fill(0);
|
|
for (auto& move_type_times : m_move_type_times)
|
|
move_type_times.fill(0.0f);
|
|
m_move_type_distances.fill(0.0f);
|
|
const size_t normal_time_mode_idx = static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal);
|
|
for (size_t i = 0; i < data.vertices.size(); ++i) {
|
|
const libvgcode::PathVertex& vertex = data.vertices[i];
|
|
const size_t move_type = static_cast<size_t>(vertex.type);
|
|
if (move_type < m_move_type_counts.size()) {
|
|
++m_move_type_counts[move_type];
|
|
for (size_t mode = 0; mode < vertex.times.size(); ++mode)
|
|
m_move_type_times[move_type][mode] += vertex.times[mode];
|
|
if (vertex.type == libvgcode::EMoveType::Retract || vertex.type == libvgcode::EMoveType::Unretract) {
|
|
m_move_type_distances[move_type] += std::fabs(vertex.feedrate) * vertex.times[normal_time_mode_idx];
|
|
} else if (i > 0) {
|
|
const float dx = vertex.position[0] - data.vertices[i - 1].position[0];
|
|
const float dy = vertex.position[1] - data.vertices[i - 1].position[1];
|
|
const float dz = vertex.position[2] - data.vertices[i - 1].position[2];
|
|
m_move_type_distances[move_type] += std::sqrt(dx * dx + dy * dy + dz * dz);
|
|
}
|
|
}
|
|
}
|
|
|
|
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_viewer.reset();
|
|
|
|
m_paths_bounding_box = BoundingBoxf3();
|
|
m_max_bounding_box = BoundingBoxf3();
|
|
m_max_print_height = 0.0f;
|
|
m_z_offset = 0.0f;
|
|
m_extruders_count = 0;
|
|
m_filament_diameters = std::vector<float>();
|
|
m_filament_densities = std::vector<float>();
|
|
m_move_type_counts.fill(0);
|
|
for (auto& move_type_times : m_move_type_times)
|
|
move_type_times.fill(0.0f);
|
|
m_move_type_distances.fill(0.0f);
|
|
m_print_statistics.reset();
|
|
m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
|
m_left_extruder_filament.clear();
|
|
m_right_extruder_filament.clear();
|
|
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();
|
|
|
|
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();
|
|
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);
|
|
const libvgcode::PathVertex& curr_vertex = m_viewer.get_current_vertex();
|
|
m_sequential_view.marker.set_world_position(libvgcode::convert(curr_vertex.position));
|
|
m_sequential_view.marker.set_z_offset(m_z_offset + 0.5f);
|
|
// 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
|
|
|
|
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);
|
|
}
|
|
|
|
// ORCA Compress consecutive duplicate speeds with 0.1 precision
|
|
auto sameSpeed = [](float a, float b) {
|
|
return static_cast<int>(std::roundf(a * 10.0f)) == static_cast<int>(std::roundf(b * 10.0f));
|
|
};
|
|
std::vector<SequentialView::ActualSpeedImguiWidget::Item> compressed;
|
|
if (!actual_speed_data.empty()) {
|
|
compressed.push_back(actual_speed_data[0]);
|
|
for (int i = 1; i < (int)actual_speed_data.size(); ++i) {
|
|
const bool same_as_prev = sameSpeed(actual_speed_data[i].speed, actual_speed_data[i - 1].speed);
|
|
const bool same_as_next = (i + 1 < (int)actual_speed_data.size()) && sameSpeed(actual_speed_data[i].speed, actual_speed_data[i + 1].speed);
|
|
if (!same_as_prev) {
|
|
if (!sameSpeed(compressed.back().speed, actual_speed_data[i - 1].speed))
|
|
compressed.push_back(actual_speed_data[i - 1]);
|
|
compressed.push_back(actual_speed_data[i]);
|
|
} else if (!same_as_next)
|
|
compressed.push_back(actual_speed_data[i]);
|
|
}
|
|
if (compressed.back().pos != actual_speed_data.back().pos)
|
|
compressed.push_back(actual_speed_data.back());
|
|
}
|
|
|
|
m_sequential_view.marker.set_actual_speed_data(compressed);
|
|
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();
|
|
const libvgcode::Interval& visible_range = m_viewer.get_view_visible_range();
|
|
uint32_t last_gcode_id = get_gcode_vertex_at(range[0]).gcode_id;
|
|
uint32_t gcode_id_min = get_gcode_vertex_at(visible_range[0]).gcode_id;
|
|
uint32_t gcode_id_max = get_gcode_vertex_at(visible_range[1]).gcode_id;
|
|
|
|
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 (alternate_values.back() == gcode_id_min)
|
|
visible_range_min_id = counter;
|
|
else if (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;
|
|
|
|
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::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];
|
|
auto offset = v->get_instance_transformation().get_matrix_no_offset().inverse() * z_offset;
|
|
v->set_volume_offset(v->get_volume_offset() + 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();
|
|
imgui.set_next_window_pos(static_cast<float>(cnv_size.get_width()), 0.0f, ImGuiCond_Always, 1.0f, 0.0f);
|
|
imgui.begin(std::string("LibVGCode Viewer Debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
|
|
|
|
if (ImGui::BeginTable("Data", 2)) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "# vertices");
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGuiWrapper::text(std::to_string(m_viewer.get_vertices_count()));
|
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "cpu memory");
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGuiWrapper::text(format_memsize(m_viewer.get_used_cpu_memory()));
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "gpu memory");
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGuiWrapper::text(format_memsize(m_viewer.get_used_gpu_memory()));
|
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "layers range");
|
|
ImGui::TableSetColumnIndex(1);
|
|
const libvgcode::Interval& layers_range = m_viewer.get_layers_view_range();
|
|
ImGuiWrapper::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);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "view range (enabled)");
|
|
ImGui::TableSetColumnIndex(1);
|
|
const libvgcode::Interval& enabled_view_range = m_viewer.get_view_enabled_range();
|
|
ImGuiWrapper::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);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "view range (visible)");
|
|
ImGui::TableSetColumnIndex(1);
|
|
const libvgcode::Interval& visible_view_range = m_viewer.get_view_visible_range();
|
|
ImGuiWrapper::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);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label);
|
|
ImGui::TableSetColumnIndex(1);
|
|
char buf[128];
|
|
sprintf(buf, "%.3f - %.3f", range[0], range[1]);
|
|
ImGuiWrapper::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("acceleration range", m_viewer.get_color_range(libvgcode::EViewType::Acceleration).get_range());
|
|
add_range_property_row("jerk range", m_viewer.get_color_range(libvgcode::EViewType::Jerk).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());
|
|
// ORCA: Add Pressure Advance visualization support
|
|
add_range_property_row("pressure advance range", m_viewer.get_color_range(libvgcode::EViewType::PressureAdvance).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);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::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);
|
|
ImGuiWrapper::text_colored(ImGuiWrapper::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];
|
|
float column_sum_m = 0.0f;
|
|
float column_sum_g = 0.0f;
|
|
if (displayed_columns & ColumnData::Model) {
|
|
const std::string weight_text = format_compact_weight(model_used_filaments_g_all_plates[i], imperial_units);
|
|
if ((displayed_columns & ~ColumnData::Model) > 0)
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", model_used_filaments_m_all_plates[i], weight_text.c_str());
|
|
else
|
|
::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", model_used_filaments_m_all_plates[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(support_used_filaments_g_all_plates[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", support_used_filaments_m_all_plates[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(flushed_filaments_g_all_plates[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", flushed_filaments_m_all_plates[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(wipe_tower_used_filaments_g_all_plates[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", wipe_tower_used_filaments_m_all_plates[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(column_sum_g, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", column_sum_m, weight_text.c_str());
|
|
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_color_arr_recommen(float window_padding)
|
|
{
|
|
ImGuiWrapper &imgui = *wxGetApp().imgui();
|
|
|
|
auto link_text = [&](const std::string &label) {
|
|
ImVec2 wiki_part_size = ImGui::CalcTextSize(label.c_str());
|
|
|
|
ImColor HyperColor = ImColor(0, 150, 136, 255).Value;
|
|
ImGui::PushStyleColor(ImGuiCol_Text, HyperColor.Value);
|
|
imgui.text(label.c_str());
|
|
ImGui::PopStyleColor();
|
|
|
|
// underline
|
|
ImVec2 lineEnd = ImGui::GetItemRectMax();
|
|
lineEnd.y -= 2.0f;
|
|
ImVec2 lineStart = lineEnd;
|
|
lineStart.x = ImGui::GetItemRectMin().x;
|
|
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, HyperColor);
|
|
// click behavior
|
|
if (ImGui::IsMouseHoveringRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), true)) {
|
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
|
Plater *plater = wxGetApp().plater();
|
|
wxCommandEvent evt(EVT_OPEN_FILAMENT_MAP_SETTINGS_DIALOG);
|
|
evt.SetEventObject(plater);
|
|
evt.SetInt(1); // 1 means from gcode viewer
|
|
wxPostEvent(plater, evt);
|
|
}
|
|
}
|
|
};
|
|
|
|
auto link_text_set_to_optional = [&](const std::string &label) {
|
|
ImVec2 wiki_part_size = ImGui::CalcTextSize(label.c_str());
|
|
|
|
ImColor HyperColor = ImColor(0, 150, 136, 255).Value;
|
|
ImGui::PushStyleColor(ImGuiCol_Text, HyperColor.Value);
|
|
imgui.text(label.c_str());
|
|
ImGui::PopStyleColor();
|
|
// underline
|
|
ImVec2 lineEnd = ImGui::GetItemRectMax();
|
|
lineEnd.y -= 2.0f;
|
|
ImVec2 lineStart = lineEnd;
|
|
lineStart.x = ImGui::GetItemRectMin().x;
|
|
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, HyperColor);
|
|
// click behavior
|
|
if (ImGui::IsMouseHoveringRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), true)) {
|
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
|
MessageDialog msg_dlg(nullptr, _L("Automatically re-slice according to the optimal filament grouping, and the grouping results will be displayed after slicing."), wxEmptyString, wxOK | wxCANCEL);
|
|
if (msg_dlg.ShowModal() == wxID_OK) {
|
|
PartPlateList &partplate_list = wxGetApp().plater()->get_partplate_list();
|
|
PartPlate *plate = partplate_list.get_curr_plate();
|
|
plate->set_filament_map_mode(FilamentMapMode::fmmAutoForFlush);
|
|
Plater *plater = wxGetApp().plater();
|
|
wxPostEvent(plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_PLATE));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
auto link_filament_group_wiki = [&](const std::string& label) {
|
|
ImVec2 wiki_part_size = ImGui::CalcTextSize(label.c_str());
|
|
ImColor HyperColor = ImColor(0, 150, 136, 255); // ORCA match color
|
|
ImGui::PushStyleColor(ImGuiCol_Text, HyperColor.Value);
|
|
imgui.text(label.c_str());
|
|
ImGui::PopStyleColor();
|
|
|
|
// ORCA use underline to match hyperlink style
|
|
ImVec2 lineEnd = ImGui::GetItemRectMax();
|
|
lineEnd.y -= 2.0f;
|
|
ImVec2 lineStart = lineEnd;
|
|
lineStart.x = ImGui::GetItemRectMin().x;
|
|
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, HyperColor);
|
|
// click behavior
|
|
if (ImGui::IsMouseHoveringRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), true)) {
|
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
|
open_filament_group_wiki();
|
|
}
|
|
}
|
|
};
|
|
|
|
auto draw_dash_line = [&](ImDrawList* draw_list, int dash_length = 5, int gap_length = 3) {
|
|
ImVec2 p1 = ImGui::GetCursorScreenPos();
|
|
ImVec2 p2 = ImVec2(p1.x + ImGui::GetContentRegionAvail().x, p1.y);
|
|
for (float i = p1.x; i < p2.x; i += (dash_length + gap_length)) {
|
|
draw_list->AddLine(ImVec2(i, p1.y), ImVec2(i + dash_length, p1.y), ImGui::GetColorU32(ImVec4(1.0f,1.0f,1.0f,0.6f))); // ORCA match color
|
|
}
|
|
};
|
|
|
|
////BBS Color Arrangement Recommendation
|
|
|
|
auto config = wxGetApp().plater()->get_partplate_list().get_current_fff_print().config();
|
|
auto stats_by_extruder = wxGetApp().plater()->get_partplate_list().get_current_fff_print().statistics_by_extruder();
|
|
|
|
float delta_weight_to_single_ext = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_curr.filament_flush_weight;
|
|
float delta_weight_to_best = stats_by_extruder.stats_by_multi_extruder_curr.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_best.filament_flush_weight;
|
|
int delta_change_to_single_ext = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_curr.filament_change_count;
|
|
int delta_change_to_best = stats_by_extruder.stats_by_multi_extruder_curr.filament_change_count - stats_by_extruder.stats_by_multi_extruder_best.filament_change_count;
|
|
|
|
bool any_less_to_single_ext = delta_weight_to_single_ext > EPSILON || delta_change_to_single_ext > 0;
|
|
bool any_more_to_best = delta_weight_to_best > EPSILON || delta_change_to_best > 0;
|
|
bool all_less_to_single_ext = delta_weight_to_single_ext > EPSILON && delta_change_to_single_ext > 0;
|
|
bool all_more_to_best = delta_weight_to_best > EPSILON && delta_change_to_best > 0;
|
|
|
|
auto get_filament_display_type = [](const ExtruderFilament& filament) {
|
|
if (filament.is_support_filament && (filament.type == "PLA" || filament.type == "PA" || filament.type == "ABS"))
|
|
return "Sup." + filament.type;
|
|
return filament.type;
|
|
};
|
|
|
|
|
|
// BBS AMS containers
|
|
float line_height = ImGui::GetFrameHeight();
|
|
float ams_item_height = 0;
|
|
float filament_group_item_align_width = 0;
|
|
{
|
|
float three_words_width = imgui.calc_text_size("ABC"sv).x;
|
|
const int line_capacity = 4;
|
|
|
|
for (const auto& extruder_filaments : {m_left_extruder_filament,m_right_extruder_filament })
|
|
{
|
|
float container_height = 0.f;
|
|
for (size_t idx = 0; idx < extruder_filaments.size(); idx += line_capacity) {
|
|
float text_line_height = 0;
|
|
for (int j = idx; j < extruder_filaments.size() && j < idx + line_capacity; ++j) {
|
|
auto text_info = imgui.calculate_filament_group_text_size(get_filament_display_type(extruder_filaments[j]));
|
|
auto text_size = std::get<0>(text_info);
|
|
filament_group_item_align_width = max(filament_group_item_align_width, text_size.x);
|
|
text_line_height = max(text_line_height, text_size.y);
|
|
}
|
|
container_height += (three_words_width * 1.3f + text_line_height );
|
|
}
|
|
container_height += 2 * line_height;
|
|
ams_item_height = std::max(ams_item_height, container_height);
|
|
}
|
|
}
|
|
|
|
int tips_count = 8;
|
|
if (any_more_to_best) {
|
|
tips_count = 8;
|
|
if (wxGetApp().app_config->get("language") != "zh_CN")
|
|
tips_count += 1;
|
|
}
|
|
else if (any_less_to_single_ext) {
|
|
tips_count = 6;
|
|
if (wxGetApp().app_config->get("language") != "zh_CN")
|
|
tips_count += 1;
|
|
}
|
|
else
|
|
tips_count = 5;
|
|
|
|
float AMS_container_height = ams_item_height + line_height * tips_count + line_height;
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // this shold be 0 since its child of gcodeviewer
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(window_padding * 3, 0));
|
|
|
|
// ImGui::Dummy({window_padding, window_padding});
|
|
ImGui::BeginChild("#AMS", ImVec2(0, AMS_container_height), true, ImGuiWindowFlags_AlwaysUseWindowPadding);
|
|
{
|
|
float available_width = ImGui::GetContentRegionAvail().x;
|
|
float half_width = available_width * 0.49f;
|
|
float spacing = 18.0f * m_scale;
|
|
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.0f,1.0f,1.0f,0.6f));
|
|
imgui.bold_text(_u8L("Filament Grouping"));
|
|
ImGui::SameLine();
|
|
std::string tip_str = _u8L("Why this grouping");
|
|
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionWidth() - window_padding - ImGui::CalcTextSize(tip_str.c_str()).x);
|
|
link_filament_group_wiki(tip_str);
|
|
ImGui::Separator();
|
|
ImGui::PopStyleColor();
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1, 1, 1, 0.05f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(window_padding * 2, window_padding));
|
|
|
|
ImDrawList *child_begin_draw_list = ImGui::GetWindowDrawList();
|
|
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
|
|
child_begin_draw_list->AddRectFilled(cursor_pos, ImVec2(cursor_pos.x + half_width, cursor_pos.y + line_height), IM_COL32(255, 255, 255, 10));
|
|
ImGui::BeginChild("#LeftAMS", ImVec2(half_width, ams_item_height), false, ImGuiWindowFlags_AlwaysUseWindowPadding);
|
|
{
|
|
imgui.text(_u8L("Left nozzle"));
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
int index = 1;
|
|
for (const auto &extruder_filament : m_left_extruder_filament) {
|
|
imgui.filament_group(get_filament_display_type(extruder_filament), extruder_filament.hex_color.c_str(), extruder_filament.filament_id, filament_group_item_align_width);
|
|
if (index % 4 != 0) { ImGui::SameLine(0, spacing); }
|
|
index++;
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::SameLine();
|
|
cursor_pos = ImGui::GetCursorScreenPos();
|
|
child_begin_draw_list->AddRectFilled(cursor_pos, ImVec2(cursor_pos.x + half_width, cursor_pos.y + line_height), IM_COL32(255, 255, 255, 10));
|
|
ImGui::BeginChild("#RightAMS", ImVec2(half_width, ams_item_height), false, ImGuiWindowFlags_AlwaysUseWindowPadding);
|
|
{
|
|
imgui.text(_u8L("Right nozzle"));
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
int index = 1;
|
|
for (const auto &extruder_filament : m_right_extruder_filament) {
|
|
imgui.filament_group(get_filament_display_type(extruder_filament), extruder_filament.hex_color.c_str(), extruder_filament.filament_id, filament_group_item_align_width);
|
|
if (index % 4 != 0) { ImGui::SameLine(0, spacing); }
|
|
index++;
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::PopStyleColor(1);
|
|
ImGui::PopStyleVar(1);
|
|
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
imgui.text_wrapped(from_u8(_u8L("Please place filaments on the printer based on grouping result.")), ImGui::GetContentRegionAvail().x);
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
|
|
{
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
draw_dash_line(draw_list);
|
|
}
|
|
|
|
bool is_optimal_group = true;
|
|
float parent_width = ImGui::GetContentRegionAvail().x;
|
|
auto number_format = [](float num) {
|
|
if (num > 1000) {
|
|
std::string number_str = std::to_string(num);
|
|
std::string first_three_digits = number_str.substr(0, 3);
|
|
return std::stoi(first_three_digits);
|
|
}
|
|
return static_cast<int>(num);
|
|
};
|
|
|
|
if (any_more_to_best) {
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
is_optimal_group = false;
|
|
ImVec4 orangeColor = ImVec4(1.0f, 0.5f, 0.0f, 1.0f);
|
|
ImGui::PushStyleColor(ImGuiCol_Text, orangeColor);
|
|
imgui.text(_u8L("Tips:"));
|
|
imgui.text(_u8L("Current grouping of slice result is not optimal."));
|
|
wxString tip;
|
|
if (delta_weight_to_best >= 0 && delta_change_to_best >= 0)
|
|
tip = from_u8((boost::format(_u8L("Increase %1%g filament and %2% changes compared to optimal grouping."))
|
|
% number_format(delta_weight_to_best)
|
|
% delta_change_to_best).str());
|
|
else if (delta_weight_to_best >= 0 && delta_change_to_best < 0)
|
|
tip = from_u8((boost::format(_u8L("Increase %1%g filament and save %2% changes compared to optimal grouping."))
|
|
% number_format(delta_weight_to_best)
|
|
% std::abs(delta_change_to_best)).str());
|
|
else if (delta_weight_to_best < 0 && delta_change_to_best >= 0)
|
|
tip = from_u8((boost::format(_u8L("Save %1%g filament and increase %2% changes compared to optimal grouping."))
|
|
% number_format(std::abs(delta_weight_to_best))
|
|
% delta_change_to_best).str());
|
|
|
|
imgui.text_wrapped(tip, parent_width);
|
|
ImGui::PopStyleColor(1);
|
|
}
|
|
else if (any_less_to_single_ext) {
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
wxString tip;
|
|
if (delta_weight_to_single_ext >= 0 && delta_change_to_single_ext >= 0)
|
|
tip = from_u8((boost::format(_u8L("Save %1%g filament and %2% changes compared to a printer with one nozzle."))
|
|
% number_format(delta_weight_to_single_ext)
|
|
% delta_change_to_single_ext).str());
|
|
else if (delta_weight_to_single_ext >= 0 && delta_change_to_single_ext < 0)
|
|
tip = from_u8((boost::format(_u8L("Save %1%g filament and increase %2% changes compared to a printer with one nozzle."))
|
|
% number_format(delta_weight_to_single_ext)
|
|
% std::abs(delta_change_to_single_ext)).str());
|
|
else if (delta_weight_to_single_ext < 0 && delta_change_to_single_ext >= 0)
|
|
tip = from_u8((boost::format(_u8L("Increase %1%g filament and save %2% changes compared to a printer with one nozzle."))
|
|
% number_format(std::abs(delta_weight_to_single_ext))
|
|
% delta_change_to_single_ext).str());
|
|
|
|
imgui.text_wrapped(tip, parent_width);
|
|
}
|
|
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
if (!is_optimal_group) {
|
|
link_text_set_to_optional(_u8L("Set to Optimal"));
|
|
ImGui::SameLine();
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
ImGui::SameLine();
|
|
}
|
|
link_text(_u8L("Regroup filament"));
|
|
|
|
ImGui::SameLine();
|
|
std::string wiki_str = _u8L("Wiki Guide"); // ORCA
|
|
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionWidth() - window_padding - ImGui::CalcTextSize(wiki_str.c_str()).x);
|
|
link_filament_group_wiki(wiki_str);
|
|
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::PopStyleColor(2);
|
|
ImGui::PopStyleVar(1);
|
|
}
|
|
|
|
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::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.f, 1.f, 1.f, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_Border, {1, 0, 0, 0});
|
|
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);
|
|
const size_t current_time_mode = static_cast<size_t>(m_viewer.get_time_mode());
|
|
const float total_estimated_time = time_mode.time > 0.0f ? time_mode.time : m_viewer.get_estimated_time();
|
|
bool show_estimated_time = total_estimated_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;
|
|
|
|
auto format_compact_count = [](unsigned long long value) {
|
|
static constexpr const char* suffixes[] = { "", "K", "M", "B", "T", "P", "E" };
|
|
constexpr size_t suffix_count = sizeof(suffixes) / sizeof(suffixes[0]);
|
|
|
|
if (value < 1000)
|
|
return std::to_string(value);
|
|
|
|
size_t suffix_index = 0;
|
|
unsigned long long divisor = 1;
|
|
while (suffix_index + 1 < suffix_count && value / divisor >= 1000) {
|
|
divisor *= 1000;
|
|
++suffix_index;
|
|
}
|
|
|
|
const unsigned long long whole = value / divisor;
|
|
const unsigned long long tenths = (value % divisor) * 10 / divisor;
|
|
|
|
std::string ret = std::to_string(whole);
|
|
if (tenths != 0)
|
|
ret += "." + std::to_string(tenths);
|
|
ret += suffixes[suffix_index];
|
|
return ret;
|
|
};
|
|
|
|
auto format_percent = [](float percent) {
|
|
if (percent == 0.0f)
|
|
return std::string("0");
|
|
|
|
char buffer[64];
|
|
percent > 0.001f ? ::sprintf(buffer, "%.1f", percent * 100.0f) : ::sprintf(buffer, "<0.1");
|
|
return std::string(buffer);
|
|
};
|
|
|
|
// 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("%s", into_u8(visible ? ImGui::VisibleIcon : ImGui::HiddenIcon).c_str());
|
|
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) + " " + _u8L("mm");
|
|
};
|
|
|
|
auto above_label = [](double z) {
|
|
char buf[64];
|
|
::sprintf(buf, "%.2f", z);
|
|
return _u8L("above") + " " + std::string(buf) + " " + _u8L("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) + " " + _u8L("mm");
|
|
};
|
|
|
|
auto role_time_and_percent = [this, total_estimated_time](libvgcode::EGCodeExtrusionRole role) {
|
|
const float time = m_viewer.get_extrusion_role_estimated_time(role);
|
|
return std::make_pair(time, total_estimated_time > 0.0f ? time / total_estimated_time : 0.0f);
|
|
};
|
|
|
|
auto travel_time_and_percent = [this, total_estimated_time]() {
|
|
const float time = m_viewer.get_travels_estimated_time();
|
|
return std::make_pair(time, total_estimated_time > 0.0f ? time / total_estimated_time : 0.0f);
|
|
};
|
|
|
|
auto format_distance = [imperial_units](float distance_mm) {
|
|
char buffer[64];
|
|
if (imperial_units) {
|
|
::sprintf(buffer, "%.2fin", distance_mm / GizmoObjectManipulation::in_to_mm);
|
|
} else if (std::fabs(distance_mm) < 1000.0f) {
|
|
::sprintf(buffer, "%.0fmm", distance_mm);
|
|
} else {
|
|
::sprintf(buffer, "%.2fm", distance_mm / 1000.0f);
|
|
}
|
|
return std::string(buffer);
|
|
};
|
|
|
|
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
|
|
|
|
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));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding , ImVec2(2.f, 2.f) * m_scale);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f * m_scale); // ORCA Match button style with combo box
|
|
|
|
// ORCA use glyph based button for fixing button sizes changing depends on used font size on platform
|
|
const wchar_t foldIcon = m_fold ? ImGui::UnfoldButtonIcon : ImGui::FoldButtonIcon;
|
|
if (imgui.glyph_button(foldIcon, ImVec2(16.f, 16.f) * m_scale)) {
|
|
m_fold = !m_fold;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
const wchar_t gCodeToggle = ImGui::gCodeButtonIcon;
|
|
if (imgui.glyph_button(gCodeToggle, ImVec2(16.f, 16.f) * m_scale)) {
|
|
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();
|
|
#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
|
|
}
|
|
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(7);
|
|
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::string travel_distance;
|
|
std::string travel_moves;
|
|
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);
|
|
used_filaments_weight.push_back(format_compact_weight(model_used_filament_g, imperial_units));
|
|
}
|
|
}
|
|
|
|
//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;
|
|
percents.push_back(travel_percent);
|
|
|
|
// Set travel distance and moves for the Travel row Usage columns
|
|
travel_distance = format_distance(m_print_statistics.total_travel_distance);
|
|
used_filaments_length.push_back(travel_distance);
|
|
|
|
travel_moves = format_compact_count(m_print_statistics.total_travel_moves);
|
|
used_filaments_weight.push_back(travel_moves);
|
|
}
|
|
|
|
// 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);
|
|
percents.pop_back();
|
|
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::Acceleration:
|
|
{
|
|
imgui.title(_u8L("Acceleration (mm/s²)"));
|
|
break;
|
|
}
|
|
case libvgcode::EViewType::Jerk:
|
|
{
|
|
imgui.title(_u8L("Jerk (mm/s)"));
|
|
break;
|
|
}
|
|
case libvgcode::EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; }
|
|
case libvgcode::EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; }
|
|
// ORCA: Add Pressure Advance visualization support
|
|
case libvgcode::EViewType::PressureAdvance:{ imgui.title(_u8L("Pressure Advance")); 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];
|
|
const std::string total_weight_text = format_compact_weight(ps.total_weight, imperial_units);
|
|
::sprintf(buffer, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", ps.total_used_filament / /*1000*/koef, total_weight_text.c_str());
|
|
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, current_time_mode, total_estimated_time, &format_compact_count, &format_percent, &format_distance](libvgcode::EOptionType type, std::vector<float> offsets) {
|
|
const bool full_layout = offsets.size() > 4;
|
|
auto option_stats = [this, current_time_mode, total_estimated_time, &format_compact_count, &format_percent, &format_distance, full_layout](libvgcode::EOptionType option_type) -> std::array<std::string, 4> {
|
|
libvgcode::EMoveType move_type;
|
|
bool has_move_type = true;
|
|
switch (option_type) {
|
|
case libvgcode::EOptionType::Wipes: { move_type = libvgcode::EMoveType::Wipe; break; }
|
|
case libvgcode::EOptionType::Retractions: { move_type = libvgcode::EMoveType::Retract; break; }
|
|
case libvgcode::EOptionType::Unretractions: { move_type = libvgcode::EMoveType::Unretract; break; }
|
|
case libvgcode::EOptionType::Seams: { move_type = libvgcode::EMoveType::Seam; break; }
|
|
case libvgcode::EOptionType::ToolChanges: { move_type = libvgcode::EMoveType::ToolChange; break; }
|
|
default: { has_move_type = false; break; }
|
|
}
|
|
|
|
if (!has_move_type)
|
|
return { "", "", "", "" };
|
|
|
|
const size_t move_type_idx = static_cast<size_t>(move_type);
|
|
float time = m_move_type_times[move_type_idx][current_time_mode];
|
|
if (option_type == libvgcode::EOptionType::ToolChanges) {
|
|
// Toolchange delays are injected via synchronize() and are not attributed to ToolChange move vertices.
|
|
time = m_print_statistics.total_filament_load_time + m_print_statistics.total_filament_unload_time + m_print_statistics.total_tool_change_time;
|
|
}
|
|
const std::string time_text = full_layout && time > 0.0f ? short_time(get_time_dhms(time)) : "";
|
|
const std::string percent_text = full_layout && total_estimated_time > 0.0f ? format_percent(time / total_estimated_time) : "";
|
|
const float seam_distance = m_print_statistics.total_seam_gap_distance + m_print_statistics.total_seam_scarf_distance;
|
|
const float distance = (option_type == libvgcode::EOptionType::Seams && seam_distance > 0.0f) ? seam_distance : m_move_type_distances[move_type_idx];
|
|
const std::string distance_text = full_layout && (option_type == libvgcode::EOptionType::Wipes || option_type == libvgcode::EOptionType::Retractions || option_type == libvgcode::EOptionType::Unretractions || option_type == libvgcode::EOptionType::Seams)
|
|
? format_distance(distance)
|
|
: "";
|
|
const std::string count_text = full_layout ? format_compact_count(m_move_type_counts[move_type_idx]) : "";
|
|
|
|
return { time_text, percent_text, distance_text, count_text };
|
|
};
|
|
|
|
auto append_option_item_with_type = [this, offsets, append_item, full_layout](libvgcode::EOptionType type, const ColorRGBA& color, const std::string& label, bool visible,
|
|
const std::string& time_text, const std::string& percent_text, const std::string& distance_text, const std::string& count_text) {
|
|
std::vector<std::pair<std::string, float>> columns_offsets;
|
|
columns_offsets.push_back({ label , offsets[0] });
|
|
if (full_layout && !time_text.empty())
|
|
columns_offsets.push_back({ time_text, offsets[1] });
|
|
if (full_layout && !percent_text.empty())
|
|
columns_offsets.push_back({ percent_text, offsets[2] });
|
|
if (full_layout && !distance_text.empty())
|
|
columns_offsets.push_back({ distance_text, offsets[3] });
|
|
if (full_layout && !count_text.empty())
|
|
columns_offsets.push_back({ count_text, distance_text.empty() ? offsets[3] : offsets[4] });
|
|
append_item(EItemType::Rect, color, columns_offsets, true, offsets.back()/*ORCA checkbox_pos*/, visible, [this, type, visible]() {
|
|
m_viewer.toggle_option_visibility(type);
|
|
update_moves_slider();
|
|
});
|
|
};
|
|
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) {
|
|
const auto option_values = option_stats(type);
|
|
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Seams)), _u8L("Seams"), visible,
|
|
option_values[0], option_values[1], option_values[2], option_values[3]);
|
|
}
|
|
else if (type == libvgcode::EOptionType::Retractions) {
|
|
const auto option_values = option_stats(type);
|
|
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Retractions)), _u8L("Retract"), visible,
|
|
option_values[0], option_values[1], option_values[2], option_values[3]);
|
|
}
|
|
else if (type == libvgcode::EOptionType::Unretractions) {
|
|
const auto option_values = option_stats(type);
|
|
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Unretractions)), _u8L("Unretract"), visible,
|
|
option_values[0], option_values[1], option_values[2], option_values[3]);
|
|
}
|
|
else if (type == libvgcode::EOptionType::ToolChanges) {
|
|
const auto option_values = option_stats(type);
|
|
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::ToolChanges)), _u8L("Filament Changes"), visible,
|
|
option_values[0], option_values[1], option_values[2], option_values[3]);
|
|
}
|
|
else if (type == libvgcode::EOptionType::Wipes) {
|
|
const auto option_values = option_stats(type);
|
|
append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Wipes)), _u8L("Wipe"), visible,
|
|
option_values[0], option_values[1], option_values[2], option_values[3]);
|
|
}
|
|
};
|
|
|
|
const libvgcode::EViewType new_view_type = curr_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]() {
|
|
m_viewer.toggle_extrusion_role_visibility(role);
|
|
update_moves_slider();
|
|
});
|
|
}
|
|
|
|
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] });
|
|
columns_offsets.push_back({ travel_distance, offsets[3] }); // Usage column
|
|
columns_offsets.push_back({ travel_moves, offsets[4] }); // Usage column
|
|
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();
|
|
});
|
|
}
|
|
}
|
|
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();
|
|
});
|
|
ImGui::PopStyleVar(1);
|
|
break;
|
|
}
|
|
case libvgcode::EViewType::ActualSpeed: {
|
|
append_range(m_viewer.get_color_range(libvgcode::EViewType::ActualSpeed), 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();
|
|
});
|
|
ImGui::PopStyleVar(1);
|
|
break;
|
|
}
|
|
case libvgcode::EViewType::Acceleration: {
|
|
append_range(m_viewer.get_color_range(libvgcode::EViewType::Acceleration), 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);
|
|
update_moves_slider();
|
|
});
|
|
ImGui::PopStyleVar(1);
|
|
break;
|
|
}
|
|
case libvgcode::EViewType::Jerk: {
|
|
append_range(m_viewer.get_color_range(libvgcode::EViewType::Jerk), 1);
|
|
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);
|
|
update_moves_slider();
|
|
});
|
|
ImGui::PopStyleVar(1);
|
|
break;
|
|
}
|
|
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; }
|
|
// ORCA: Add Pressure Advance visualization support
|
|
case libvgcode::EViewType::PressureAdvance: { append_range(m_viewer.get_color_range(libvgcode::EViewType::PressureAdvance), 3); 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) {
|
|
const std::string weight_text = format_compact_weight(model_used_filaments_g[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", model_used_filaments_m[i], weight_text.c_str());
|
|
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::Summary:
|
|
{
|
|
char buf[64];
|
|
imgui.text(_u8L("Total") + ":");
|
|
ImGui::SameLine();
|
|
const std::string total_weight_text = format_compact_weight(ps.total_weight, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in / %s" : "%.2f m / %s", ps.total_used_filament / koef, total_weight_text.c_str());
|
|
imgui.text(buf);
|
|
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
ImGui::SameLine();
|
|
imgui.text(_u8L("Cost") + ":");
|
|
ImGui::SameLine();
|
|
::sprintf(buf, "%.2f", ps.total_cost);
|
|
imgui.text(buf);
|
|
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
ImGui::SameLine();
|
|
imgui.text(_u8L("Total time") + ":");
|
|
ImGui::SameLine();
|
|
imgui.text(short_time(get_time_dhms(time_mode.time)));
|
|
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();
|
|
auto tool_colors = m_viewer.get_tool_colors();
|
|
for (auto extruder_idx : used_extruders_ids) {
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(model_used_filaments_g[i], imperial_units);
|
|
if ((displayed_columns & ~ColumnData::Model) > 0)
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", model_used_filaments_m[i], weight_text.c_str());
|
|
else
|
|
::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", model_used_filaments_m[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(support_used_filaments_g[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", support_used_filaments_m[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(flushed_filaments_g[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", flushed_filaments_m[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(wipe_tower_used_filaments_g[i], imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", wipe_tower_used_filaments_m[i], weight_text.c_str());
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(column_sum_g, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", column_sum_m, weight_text.c_str());
|
|
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, libvgcode::convert(tool_colors[extruder_idx]), columns_offsets, false, checkbox_pos/*ORCA*/, true, [this, extruder_idx]() {});
|
|
}
|
|
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) {
|
|
const std::string weight_text = format_compact_weight(total_model_used_filament_g, imperial_units);
|
|
if ((displayed_columns & ~ColumnData::Model) > 0)
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_model_used_filament_m, weight_text.c_str());
|
|
else
|
|
::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", total_model_used_filament_m, weight_text.c_str());
|
|
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] });
|
|
}
|
|
if (displayed_columns & ColumnData::Support) {
|
|
const std::string weight_text = format_compact_weight(total_support_used_filament_g, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_support_used_filament_m, weight_text.c_str());
|
|
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] });
|
|
}
|
|
if (displayed_columns & ColumnData::Flushed) {
|
|
const std::string weight_text = format_compact_weight(total_flushed_filament_g, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_flushed_filament_m, weight_text.c_str());
|
|
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")] });
|
|
}
|
|
if (displayed_columns & ColumnData::WipeTower) {
|
|
const std::string weight_text = format_compact_weight(total_wipe_tower_used_filament_g, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_wipe_tower_used_filament_m, weight_text.c_str());
|
|
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] });
|
|
}
|
|
if ((displayed_columns & ~ColumnData::Model) > 0) {
|
|
const std::string weight_text = format_compact_weight(total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g, imperial_units);
|
|
::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m,
|
|
weight_text.c_str());
|
|
columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] });
|
|
}
|
|
append_item(EItemType::None, libvgcode::convert(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();
|
|
imgui.text(format_compact_count(m_print_statistics.total_filament_changes));
|
|
|
|
//display tool change times
|
|
ImGui::Dummy({window_padding, window_padding});
|
|
ImGui::SameLine();
|
|
imgui.text(_u8L("Tool changes") + ":");
|
|
ImGui::SameLine();
|
|
imgui.text(format_compact_count(m_print_statistics.total_extruder_changes));
|
|
|
|
//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]);
|
|
imgui.text(format_compact_weight(used_filament.second, imperial_units));
|
|
}
|
|
};
|
|
|
|
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::ActualSpeed:
|
|
case libvgcode::EViewType::Acceleration:
|
|
case libvgcode::EViewType::Jerk:
|
|
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 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);
|
|
|
|
// ORCA: Get layer Zs as doubles
|
|
std::vector<double> layer_zs = get_layers_zs();
|
|
|
|
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 = find_close_layer_idx(layer_zs, custom_gcode.print_z, epsilon()); // ORCA: find layer index by Z
|
|
::sprintf(buf, "%d", layer + 1); // +1 because layer 0 is the first 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; i++) { // sum all previous layers time
|
|
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();
|
|
imgui.text(" " + format_compact_weight(ps.total_weight, imperial_units));
|
|
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();
|
|
imgui.text(" " + format_compact_weight(ps.total_weight - exlude_g, imperial_units));
|
|
//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);
|
|
}
|
|
ImGui::Dummy({ window_padding, window_padding });
|
|
if (m_nozzle_nums > 1 && (m_viewer.get_view_type() == libvgcode::EViewType::Summary || m_viewer.get_view_type() == libvgcode::EViewType::ColorPrint)) // ORCA show only on summary and filament tab
|
|
render_legend_color_arr_recommen(window_padding);
|
|
|
|
legend_height = ImGui::GetCurrentWindow()->Size.y;
|
|
imgui.end();
|
|
ImGui::PopStyleColor(7);
|
|
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
|
|
|