mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-20 15:21:21 -06:00
Merge some BS1.7 changes:
Boolean operation feature
This commit is contained in:
parent
9e73dfeb5d
commit
da05ae02da
73 changed files with 2496 additions and 481 deletions
|
@ -341,8 +341,9 @@ const float bed3d_ax3s_default_tip_length = 5.0f;
|
|||
int CLI::run(int argc, char **argv)
|
||||
{
|
||||
// Mark the main thread for the debugger and for runtime checks.
|
||||
set_current_thread_name("bambustu_main");
|
||||
|
||||
set_current_thread_name("orcaslicer_main");
|
||||
// Save the thread ID of the main thread.
|
||||
save_main_thread_id();
|
||||
#ifdef __WXGTK__
|
||||
// On Linux, wxGTK has no support for Wayland, and the app crashes on
|
||||
// startup if gtk3 is used. This env var has to be set explicitly to
|
||||
|
@ -2993,7 +2994,7 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
|
|||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
extern "C" {
|
||||
__declspec(dllexport) int __stdcall bambustu_main(int argc, wchar_t **argv)
|
||||
__declspec(dllexport) int __stdcall orcaslicer_main(int argc, wchar_t **argv)
|
||||
{
|
||||
// Convert wchar_t arguments to UTF8.
|
||||
std::vector<std::string> argv_narrow;
|
||||
|
|
|
@ -204,7 +204,7 @@ bool OpenGLVersionCheck::message_pump_exit = false;
|
|||
|
||||
extern "C" {
|
||||
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv);
|
||||
Slic3rMainFunc bambustu_main = nullptr;
|
||||
Slic3rMainFunc orcaslicer_main = nullptr;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -292,19 +292,19 @@ int wmain(int argc, wchar_t **argv)
|
|||
}
|
||||
|
||||
// resolve function address here
|
||||
bambustu_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
|
||||
orcaslicer_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
|
||||
#ifdef _WIN64
|
||||
// there is just a single calling conversion, therefore no mangling of the function name.
|
||||
"bambustu_main"
|
||||
"orcaslicer_main"
|
||||
#else // stdcall calling convention declaration
|
||||
"_bambustu_main@8"
|
||||
#endif
|
||||
);
|
||||
if (bambustu_main == nullptr) {
|
||||
printf("could not locate the function bambustu_main in OrcaSlicer.dll\n");
|
||||
if (orcaslicer_main == nullptr) {
|
||||
printf("could not locate the function orcaslicer_main in OrcaSlicer.dll\n");
|
||||
return -1;
|
||||
}
|
||||
// argc minus the trailing nullptr of the argv
|
||||
return bambustu_main((int)argv_extended.size() - 1, argv_extended.data());
|
||||
return orcaslicer_main((int)argv_extended.size() - 1, argv_extended.data());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,6 +193,10 @@ namespace ImGui
|
|||
const wchar_t CollapseBtn = 0x0831;
|
||||
const wchar_t RevertBtn = 0x0832;
|
||||
|
||||
const wchar_t CloseBlockNotifButton = 0x0833;
|
||||
const wchar_t CloseBlockNotifHoverButton = 0x0834;
|
||||
const wchar_t BlockNotifErrorIcon = 0x0835;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
|
|
@ -461,6 +461,11 @@ public:
|
|||
const PrintObjects& objects() const { return m_objects; }
|
||||
// PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects
|
||||
// in the notification center.
|
||||
const SLAPrintObject* get_print_object_by_model_object_id(ObjectID object_id) const {
|
||||
auto it = std::find_if(m_objects.begin(), m_objects.end(),
|
||||
[object_id](const SLAPrintObject* obj) { return obj->model_object()->id() == object_id; });
|
||||
return (it == m_objects.end()) ? nullptr : *it;
|
||||
}
|
||||
const SLAPrintObject* get_object(ObjectID object_id) const {
|
||||
auto it = std::find_if(m_objects.begin(), m_objects.end(),
|
||||
[object_id](const SLAPrintObject *obj) { return obj->id() == object_id; });
|
||||
|
|
|
@ -137,6 +137,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Gizmos/GLGizmoSeam.hpp
|
||||
GUI/Gizmos/GLGizmoText.cpp
|
||||
GUI/Gizmos/GLGizmoText.hpp
|
||||
GUI/Gizmos/GLGizmoMeshBoolean.cpp
|
||||
GUI/Gizmos/GLGizmoMeshBoolean.hpp
|
||||
GUI/GLSelectionRectangle.cpp
|
||||
GUI/GLSelectionRectangle.hpp
|
||||
GUI/Gizmos/GizmoObjectManipulation.cpp
|
||||
|
|
|
@ -71,30 +71,34 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char
|
|||
// BBS
|
||||
std::vector<std::array<float, 4>> get_extruders_colors()
|
||||
{
|
||||
unsigned char rgb_color[3] = {};
|
||||
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
|
||||
unsigned char rgba_color[4] = {};
|
||||
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
|
||||
std::vector<std::array<float, 4>> colors_out(colors.size());
|
||||
for (const std::string& color : colors) {
|
||||
Slic3r::GUI::BitmapCache::parse_color(color, rgb_color);
|
||||
size_t color_idx = &color - &colors.front();
|
||||
colors_out[color_idx] = { float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f };
|
||||
for (const std::string &color : colors) {
|
||||
Slic3r::GUI::BitmapCache::parse_color4(color, rgba_color);
|
||||
size_t color_idx = &color - &colors.front();
|
||||
colors_out[color_idx] = {
|
||||
float(rgba_color[0]) / 255.f,
|
||||
float(rgba_color[1]) / 255.f,
|
||||
float(rgba_color[2]) / 255.f,
|
||||
float(rgba_color[3]) / 255.f,
|
||||
};
|
||||
}
|
||||
|
||||
return colors_out;
|
||||
}
|
||||
|
||||
std::array<float, 4> adjust_color_for_rendering(const std::array<float, 4>& colors)
|
||||
float FullyTransparentMaterialThreshold = 0.1f;
|
||||
float FullTransparentModdifiedToFixAlpha = 0.3f;
|
||||
std::array<float, 4> adjust_color_for_rendering(const std::array<float, 4> &colors)
|
||||
{
|
||||
if ((colors[0] < 0.1) && (colors[1] < 0.1) && (colors[2] < 0.1))
|
||||
{
|
||||
std::array<float, 4> new_color;
|
||||
new_color[0] = 0.1;
|
||||
new_color[1] = 0.1;
|
||||
new_color[2] = 0.1;
|
||||
new_color[3] = colors[3];
|
||||
return new_color;
|
||||
}
|
||||
|
||||
if (colors[3] < FullyTransparentMaterialThreshold) { // completely transparent
|
||||
std::array<float, 4> new_color;
|
||||
new_color[0] = 1;
|
||||
new_color[1] = 1;
|
||||
new_color[2] = 1;
|
||||
new_color[3] = FullTransparentModdifiedToFixAlpha;
|
||||
return new_color;
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
|
@ -514,8 +518,13 @@ void GLVolume::set_render_color()
|
|||
}
|
||||
}
|
||||
|
||||
if (force_transparent)
|
||||
render_color[3] = color[3];
|
||||
if (force_transparent) {
|
||||
if (color[3] < FullyTransparentMaterialThreshold) {
|
||||
render_color[3] = FullTransparentModdifiedToFixAlpha;
|
||||
} else {
|
||||
render_color[3] = color[3];
|
||||
}
|
||||
}
|
||||
|
||||
//BBS set unprintable color
|
||||
if (!printable) {
|
||||
|
@ -1017,12 +1026,21 @@ void GLWipeTowerVolume::render(bool with_outline) const
|
|||
}
|
||||
this->iva_per_colors[i].render();
|
||||
}
|
||||
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
if (this->is_left_handed())
|
||||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
bool GLWipeTowerVolume::IsTransparent() {
|
||||
for (size_t i = 0; i < m_colors.size(); i++) {
|
||||
if (m_colors[i][3] < 1.0f) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<int> GLVolumeCollection::load_object(
|
||||
const ModelObject *model_object,
|
||||
int obj_idx,
|
||||
|
@ -1211,8 +1229,12 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
|
|||
for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i) {
|
||||
GLVolume* volume = volumes[i];
|
||||
bool is_transparent = (volume->render_color[3] < 1.0f);
|
||||
if (((type == GLVolumeCollection::ERenderType::Opaque && !is_transparent) ||
|
||||
(type == GLVolumeCollection::ERenderType::Transparent && is_transparent) ||
|
||||
auto tempGlwipeTowerVolume = dynamic_cast<GLWipeTowerVolume *>(volume);
|
||||
if (tempGlwipeTowerVolume) {
|
||||
is_transparent = tempGlwipeTowerVolume->IsTransparent();
|
||||
}
|
||||
if (((type == GLVolumeCollection::ERenderType::Opaque && !is_transparent) ||
|
||||
(type == GLVolumeCollection::ERenderType::Transparent && is_transparent) ||
|
||||
type == GLVolumeCollection::ERenderType::All) &&
|
||||
(! filter_func || filter_func(*volume)))
|
||||
list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0)));
|
||||
|
@ -1236,8 +1258,17 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
|
|||
return list;
|
||||
}
|
||||
|
||||
int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_support) const
|
||||
{
|
||||
const DynamicPrintConfig& glb_cfg = GUI::wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
enable_support = glb_cfg.opt_bool("enable_support");
|
||||
int support_threshold_angle = glb_cfg.opt_int("support_threshold_angle");
|
||||
return support_threshold_angle ;
|
||||
}
|
||||
|
||||
//BBS: add outline drawing logic
|
||||
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func, bool with_outline) const
|
||||
void GLVolumeCollection::render(
|
||||
GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d &view_matrix, std::function<bool(const GLVolume &)> filter_func, bool with_outline) const
|
||||
{
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
|
||||
if (to_render.empty())
|
||||
|
@ -1305,10 +1336,16 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
//use -1 ad a invalid type
|
||||
shader->set_uniform("print_volume.type", -1);
|
||||
}
|
||||
|
||||
bool enable_support;
|
||||
int support_threshold_angle = get_selection_support_threshold_angle(enable_support);
|
||||
|
||||
float normal_z = -::cos(Geometry::deg2rad((float) support_threshold_angle));
|
||||
|
||||
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
|
||||
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
||||
shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||
shader->set_uniform("slope.normal_z", m_slope.normal_z);
|
||||
shader->set_uniform("slope.normal_z", normal_z);
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
|
||||
|
@ -1496,34 +1533,35 @@ void GLVolumeCollection::reset_outside_state()
|
|||
}
|
||||
}
|
||||
|
||||
void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config)
|
||||
void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha)
|
||||
{
|
||||
static const float inv_255 = 1.0f / 255.0f;
|
||||
|
||||
struct Color
|
||||
{
|
||||
std::string text;
|
||||
unsigned char rgb[3];
|
||||
unsigned char rgba[4];
|
||||
|
||||
Color()
|
||||
: text("")
|
||||
{
|
||||
rgb[0] = 255;
|
||||
rgb[1] = 255;
|
||||
rgb[2] = 255;
|
||||
rgba[0] = 255;
|
||||
rgba[1] = 255;
|
||||
rgba[2] = 255;
|
||||
rgba[3] = 255;
|
||||
}
|
||||
|
||||
void set(const std::string& text, unsigned char* rgb)
|
||||
void set(const std::string& text, unsigned char* rgba)
|
||||
{
|
||||
this->text = text;
|
||||
::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char));
|
||||
::memcpy((void*)this->rgba, (const void*)rgba, 4 * sizeof(unsigned char));
|
||||
}
|
||||
};
|
||||
|
||||
if (config == nullptr)
|
||||
return;
|
||||
|
||||
unsigned char rgb[3];
|
||||
unsigned char rgba[4];
|
||||
std::vector<Color> colors;
|
||||
|
||||
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA)
|
||||
|
@ -1531,9 +1569,9 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
|||
const std::string& txt_color = config->opt_string("material_colour").empty() ?
|
||||
print_config_def.get("material_colour")->get_default_value<ConfigOptionString>()->value :
|
||||
config->opt_string("material_colour");
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) {
|
||||
if (Slic3r::GUI::BitmapCache::parse_color4(txt_color, rgba)) {
|
||||
colors.resize(1);
|
||||
colors[0].set(txt_color, rgb);
|
||||
colors[0].set(txt_color, rgba);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1549,8 +1587,8 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
|||
|
||||
for (unsigned int i = 0; i < colors_count; ++i) {
|
||||
const std::string& txt_color = config->opt_string("filament_colour", i);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color4(txt_color, rgba))
|
||||
colors[i].set(txt_color, rgba);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1564,8 +1602,14 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
|||
|
||||
const Color& color = colors[extruder_id];
|
||||
if (!color.text.empty()) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
volume->color[i] = (float)color.rgb[i] * inv_255;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (is_update_alpha == false) {
|
||||
if (i < 3) {
|
||||
volume->color[i] = (float) color.rgba[i] * inv_255;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
volume->color[i] = (float) color.rgba[i] * inv_255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
#define glcheck()
|
||||
#endif // HAS_GLSAFE
|
||||
extern std::vector<std::array<float, 4>> get_extruders_colors();
|
||||
extern std::array<float, 4> adjust_color_for_rendering(const std::array<float, 4>& colors);
|
||||
extern float FullyTransparentMaterialThreshold;
|
||||
extern float FullTransparentModdifiedToFixAlpha;
|
||||
extern std::array<float, 4> adjust_color_for_rendering(const std::array<float, 4> &colors);
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -558,6 +560,7 @@ public:
|
|||
virtual void render(bool with_outline = false) const;
|
||||
|
||||
std::vector<GLIndexedVertexArray> iva_per_colors;
|
||||
bool IsTransparent();
|
||||
|
||||
private:
|
||||
std::vector<std::array<float, 4>> m_colors;
|
||||
|
@ -604,7 +607,8 @@ private:
|
|||
struct Slope
|
||||
{
|
||||
// toggle for slope rendering
|
||||
bool active{ false };
|
||||
bool active{ false };//local active
|
||||
bool isGlobalActive{false};
|
||||
float normal_z;
|
||||
};
|
||||
|
||||
|
@ -656,9 +660,14 @@ public:
|
|||
GLVolume* new_toolpath_volume(const std::array<float, 4>& rgba, size_t reserve_vbo_floats = 0);
|
||||
GLVolume* new_nontoolpath_volume(const std::array<float, 4>& rgba, size_t reserve_vbo_floats = 0);
|
||||
|
||||
int get_selection_support_threshold_angle(bool&) const;
|
||||
// Render the volumes by OpenGL.
|
||||
//BBS: add outline drawing logic
|
||||
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>(), bool with_outline = true) const;
|
||||
void render(ERenderType type,
|
||||
bool disable_cullface,
|
||||
const Transform3d & view_matrix,
|
||||
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(),
|
||||
bool with_outline = true) const;
|
||||
|
||||
// Finalize the initialization of the geometry & indices,
|
||||
// upload the geometry and indices to OpenGL VBO objects
|
||||
|
@ -678,8 +687,10 @@ public:
|
|||
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
|
||||
|
||||
bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; }
|
||||
bool is_slope_active() const { return m_slope.active; }
|
||||
void set_slope_active(bool active) { m_slope.active = active; }
|
||||
void set_slope_GlobalActive(bool active) { m_slope.isGlobalActive = active; }
|
||||
|
||||
float get_slope_normal_z() const { return m_slope.normal_z; }
|
||||
void set_slope_normal_z(float normal_z) { m_slope.normal_z = normal_z; }
|
||||
|
@ -691,7 +702,7 @@ public:
|
|||
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
void reset_outside_state();
|
||||
|
||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||
void update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha = true);
|
||||
|
||||
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
|
||||
std::vector<double> get_current_print_zs(bool active_only) const;
|
||||
|
|
|
@ -46,9 +46,10 @@ bool SlicingProcessCompletedEvent::critical_error() const
|
|||
} catch (const Slic3r::SlicingError &) {
|
||||
// Exception derived from SlicingError is non-critical.
|
||||
return false;
|
||||
} catch (...) {
|
||||
}
|
||||
return true;
|
||||
} catch (const Slic3r::SlicingErrors &) {
|
||||
return false;
|
||||
} catch (...) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SlicingProcessCompletedEvent::invalidate_plater() const
|
||||
|
@ -69,7 +70,7 @@ bool SlicingProcessCompletedEvent::invalidate_plater() const
|
|||
return false;
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> SlicingProcessCompletedEvent::format_error_message() const
|
||||
std::pair<std::string, std::vector<size_t>> SlicingProcessCompletedEvent::format_error_message() const
|
||||
{
|
||||
std::string error;
|
||||
size_t monospace = 0;
|
||||
|
@ -88,12 +89,20 @@ std::pair<std::string, size_t> SlicingProcessCompletedEvent::format_error_messag
|
|||
} catch (SlicingError &ex) {
|
||||
error = ex.what();
|
||||
monospace = ex.objectId();
|
||||
} catch (SlicingErrors &exs) {
|
||||
std::vector<size_t> ids;
|
||||
for (auto &ex : exs.errors_) {
|
||||
error = ex.what();
|
||||
monospace = ex.objectId();
|
||||
ids.push_back(monospace);
|
||||
}
|
||||
return std::make_pair(std::move(error), ids);
|
||||
} catch (std::exception &ex) {
|
||||
error = ex.what();
|
||||
} catch (...) {
|
||||
error = "Unknown C++ exception.";
|
||||
}
|
||||
return std::make_pair(std::move(error), monospace);
|
||||
error = ex.what();
|
||||
} catch (...) {
|
||||
error = "Unknown C++ exception.";
|
||||
}
|
||||
return std::make_pair(std::move(error), std::vector<size_t>{monospace});
|
||||
}
|
||||
|
||||
BackgroundSlicingProcess::BackgroundSlicingProcess()
|
||||
|
@ -308,6 +317,8 @@ void BackgroundSlicingProcess::thread_proc()
|
|||
break;
|
||||
// Process the background slicing task.
|
||||
m_state = STATE_RUNNING;
|
||||
//BBS: internal cancel
|
||||
m_internal_cancelled = false;
|
||||
lck.unlock();
|
||||
std::exception_ptr exception;
|
||||
#ifdef _WIN32
|
||||
|
@ -328,6 +339,10 @@ void BackgroundSlicingProcess::thread_proc()
|
|||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": send SlicingProcessCompletedEvent to main, status %1%")%evt.status();
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
}
|
||||
else {
|
||||
//BBS: internal cancel
|
||||
m_internal_cancelled = true;
|
||||
}
|
||||
m_print->restart();
|
||||
lck.unlock();
|
||||
// Let the UI thread wake up if it is waiting for the background task to finish.
|
||||
|
|
|
@ -60,7 +60,7 @@ public:
|
|||
void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); }
|
||||
// Produce a human readable message to be displayed by a notification or a message box.
|
||||
// 2nd parameter defines whether the output should be displayed with a monospace font.
|
||||
std::pair<std::string, size_t> format_error_message() const;
|
||||
std::pair<std::string, std::vector<size_t>> format_error_message() const;
|
||||
|
||||
private:
|
||||
StatusType m_status;
|
||||
|
@ -185,6 +185,7 @@ public:
|
|||
//BBS: improve the finished logic, also judge the m_gcode_result
|
||||
//bool finished() const { return m_print->finished(); }
|
||||
bool finished() const { return m_print->finished() && !m_gcode_result->moves.empty(); }
|
||||
bool is_internal_cancelled() { return m_internal_cancelled; }
|
||||
|
||||
//BBS: add Plater to friend class
|
||||
//need to call stop_internal in ui thread
|
||||
|
@ -275,6 +276,7 @@ private:
|
|||
//BBS: partplate related
|
||||
GUI::PartPlate* m_current_plate;
|
||||
PrinterTechnology m_printer_tech = ptUnknown;
|
||||
bool m_internal_cancelled = false;
|
||||
|
||||
PrintState<BackgroundSlicingProcessStep, bspsCount> m_step_state;
|
||||
bool set_step_started(BackgroundSlicingProcessStep step);
|
||||
|
|
|
@ -375,7 +375,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
|
|||
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
// BBS: support resize by fill border
|
||||
if (scale_in_center > 0) {
|
||||
if (scale_in_center > 0 && scale_in_center < svg_scale) {
|
||||
int w = (int)(image->width * scale_in_center);
|
||||
int h = (int)(image->height * scale_in_center);
|
||||
::nsvgRasterize(rast, image, 0, 0, scale_in_center, data.data() + int(height - h) / 2 * width * 4 + int(width - w) / 2 * 4, w, h, width * 4);
|
||||
|
@ -433,6 +433,14 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
|
|||
|
||||
bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
|
||||
{
|
||||
if (scolor.size() == 9) {
|
||||
unsigned char rgba[4];
|
||||
parse_color4(scolor, rgba);
|
||||
rgb_out[0] = rgba[0];
|
||||
rgb_out[1] = rgba[1];
|
||||
rgb_out[2] = rgba[2];
|
||||
return true;
|
||||
}
|
||||
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
|
||||
if (scolor.size() != 7 || scolor.front() != '#')
|
||||
return false;
|
||||
|
@ -444,6 +452,23 @@ bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
|
|||
return false;
|
||||
rgb_out[i] = (unsigned char)(digit1 * 16 + digit2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmapCache::parse_color4(const std::string& scolor, unsigned char* rgba_out)
|
||||
{
|
||||
rgba_out[0] = rgba_out[1] = rgba_out[2] = 0; rgba_out[3] = 255;
|
||||
if ((scolor.size() != 7 && scolor.size() != 9) || scolor.front() != '#')
|
||||
return false;
|
||||
const char* c = scolor.data() + 1;
|
||||
for (size_t i = 0; i < scolor.size() / 2; ++i) {
|
||||
int digit1 = hex_digit_to_int(*c++);
|
||||
int digit2 = hex_digit_to_int(*c++);
|
||||
if (digit1 == -1 || digit2 == -1)
|
||||
return false;
|
||||
rgba_out[i] = (unsigned char)(digit1 * 16 + digit2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); }
|
||||
|
||||
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
|
||||
static bool parse_color4(const std::string& scolor, unsigned char* rgba_out);
|
||||
|
||||
private:
|
||||
std::map<std::string, wxBitmap*> m_map;
|
||||
|
|
|
@ -835,10 +835,12 @@ public:
|
|||
bool can_export_toolpaths() const;
|
||||
std::vector<int> get_plater_extruder();
|
||||
|
||||
const float get_max_print_height() const { return m_max_print_height; }
|
||||
const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
|
||||
const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
|
||||
const BoundingBoxf3& get_shell_bounding_box() const { return m_shell_bounding_box; }
|
||||
const std::vector<double>& get_layers_zs() const { return m_layers.get_zs(); }
|
||||
const std::array<unsigned int,2> &get_layers_z_range() const { return m_layers_z_range; }
|
||||
|
||||
const SequentialView& get_sequential_view() const { return m_sequential_view; }
|
||||
void update_sequential_view_current(unsigned int first, unsigned int last);
|
||||
|
|
|
@ -748,7 +748,7 @@ void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_
|
|||
}
|
||||
|
||||
// updates print order strings
|
||||
if (sorted_instances.size() > 1) {
|
||||
if (sorted_instances.size() > 0) {
|
||||
for (size_t i = 0; i < sorted_instances.size(); ++i) {
|
||||
size_t id = sorted_instances[i]->id().id;
|
||||
std::vector<Owner>::iterator it = std::find_if(owners.begin(), owners.end(), [id](const Owner& owner) {
|
||||
|
@ -1296,6 +1296,15 @@ void GLCanvas3D::on_change_color_mode(bool is_dark, bool reinit) {
|
|||
m_gizmos.set_icon_dirty();
|
||||
}
|
||||
}
|
||||
if (m_canvas_type == CanvasAssembleView) {
|
||||
m_gizmos.on_change_color_mode(is_dark);
|
||||
if (reinit) {
|
||||
// reset svg
|
||||
m_gizmos.switch_gizmos_icon_filename();
|
||||
// set dirty to re-generate icon texture
|
||||
m_gizmos.set_icon_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_as_dirty()
|
||||
|
@ -1614,6 +1623,8 @@ void GLCanvas3D::enable_main_toolbar(bool enable)
|
|||
void GLCanvas3D::reset_select_plate_toolbar_selection() {
|
||||
if (m_sel_plate_toolbar.m_all_plates_stats_item)
|
||||
m_sel_plate_toolbar.m_all_plates_stats_item->selected = false;
|
||||
if (wxGetApp().mainframe)
|
||||
wxGetApp().mainframe->update_slice_print_status(MainFrame::eEventSliceUpdate, true, true);
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_select_plate_toolbar(bool enable)
|
||||
|
@ -1887,32 +1898,30 @@ void GLCanvas3D::render(bool only_init)
|
|||
_render_selection();
|
||||
if (!no_partplate)
|
||||
_render_bed(!camera.is_looking_downward(), show_axes);
|
||||
//BBS: add outline logic
|
||||
if (!no_partplate) //BBS: add outline logic
|
||||
_render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id, true);
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
if (!no_partplate)
|
||||
_render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id);
|
||||
}
|
||||
/* preview render */
|
||||
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
//BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
_render_gcode(cnv_size.get_width(), cnv_size.get_height());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_bed(!camera.is_looking_downward(), show_axes);
|
||||
_render_platelist(!camera.is_looking_downward(), only_current, true, hover_id);
|
||||
// BBS: GUI refactor: add canvas size as parameters
|
||||
_render_gcode(cnv_size.get_width(), cnv_size.get_height());
|
||||
}
|
||||
/* assemble render*/
|
||||
else if (m_canvas_type == ECanvasType::CanvasAssembleView) {
|
||||
//BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
//_render_bed(!camera.is_looking_downward(), show_axes);
|
||||
//BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_plane();
|
||||
//BBS: add outline logic insteadof selection under assemble view
|
||||
//_render_selection();
|
||||
// BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
}
|
||||
|
||||
_render_sequential_clearance();
|
||||
|
@ -2377,9 +2386,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
deleted_wipe_towers.emplace_back(volume, volume_id);
|
||||
delete volume;
|
||||
}
|
||||
|
||||
// BBS
|
||||
m_explosion_ratio = 1.0;
|
||||
}
|
||||
else {
|
||||
// This GLVolume will be reused.
|
||||
|
@ -2635,7 +2641,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
|
||||
const float margin = 15.f;
|
||||
BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_id)->get_bounding_box();
|
||||
coordf_t plate_bbox_x_max_local_coord = plate_bbox.max(0) - plate_origin(0);
|
||||
coordf_t plate_bbox_y_max_local_coord = plate_bbox.max(1) - plate_origin(1);
|
||||
if (x + margin + wipe_tower_size(0) > plate_bbox_x_max_local_coord) {
|
||||
x = plate_bbox_x_max_local_coord - wipe_tower_size(0) - margin;
|
||||
ConfigOptionFloat wt_x_opt(x);
|
||||
dynamic_cast<ConfigOptionFloats *>(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_id, 0);
|
||||
}
|
||||
if (y + margin + wipe_tower_size(1) > plate_bbox_y_max_local_coord) {
|
||||
y = plate_bbox_y_max_local_coord - wipe_tower_size(1) - margin;
|
||||
ConfigOptionFloat wt_y_opt(y);
|
||||
|
@ -2673,6 +2685,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
|
||||
//BBS:exclude the assmble view
|
||||
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
|
||||
_set_warning_notification_if_needed(EWarning::GCodeConflict);
|
||||
// checks for geometry outside the print volume to render it accordingly
|
||||
if (!m_volumes.empty()) {
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
|
@ -2758,6 +2771,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co
|
|||
if (wxGetApp().is_editor()) {
|
||||
//BBS: always load shell at preview, do this in load_shells
|
||||
//m_gcode_viewer.update_shells_color_by_extruder(m_config);
|
||||
_set_warning_notification_if_needed(EWarning::ToolHeightOutside);
|
||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||
_set_warning_notification_if_needed(EWarning::GCodeConflict);
|
||||
}
|
||||
|
@ -2902,6 +2916,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||
// BBS
|
||||
//m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
|
||||
m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
|
||||
_update_imgui_select_plate_toolbar();
|
||||
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
m_dirty |= mouse3d_controller_applied;
|
||||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
|
@ -3051,7 +3066,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_Y:
|
||||
#endif /* __APPLE__ */
|
||||
if (m_canvas_type == CanvasView3D) {
|
||||
if (m_canvas_type == CanvasView3D || m_canvas_type == CanvasAssembleView) {
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_REDO));
|
||||
}
|
||||
break;
|
||||
|
@ -3062,7 +3077,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
case WXK_CONTROL_Z:
|
||||
#endif /* __APPLE__ */
|
||||
// only support redu/undo in CanvasView3D
|
||||
if (m_canvas_type == CanvasView3D) {
|
||||
if (m_canvas_type == CanvasView3D || m_canvas_type == CanvasAssembleView) {
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_UNDO));
|
||||
}
|
||||
break;
|
||||
|
@ -3477,7 +3492,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
}
|
||||
else if (keyCode == WXK_CONTROL)
|
||||
m_dirty = true;
|
||||
else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) {
|
||||
else if (keyCode == WXK_TAB && evt.ShiftDown() && !evt.ControlDown() && !wxGetApp().is_gcode_viewer()) {
|
||||
// Collapse side-panel with Shift+Tab
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR));
|
||||
} else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) {
|
||||
auto _do_rotate = [this](double angle_z_rad) {
|
||||
m_selection.start_dragging();
|
||||
m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint));
|
||||
|
@ -3769,7 +3787,7 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt)
|
|||
float z = 0;
|
||||
const Vec3d &p2 = _mouse_to_3d({p.x, p.y}, &z);
|
||||
const Vec3d &p1 = _mouse_to_3d({p.x - d.x, p.y - d.y}, &z);
|
||||
camera.set_target(camera.get_target() + p2 - p1);
|
||||
camera.set_target(camera.get_target() + p1 - p2);
|
||||
} else if (evt.GetEventType() == wxEVT_GESTURE_ZOOM) {
|
||||
static float zoom_start = 1;
|
||||
if (evt.IsGestureStart())
|
||||
|
@ -4151,7 +4169,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
// if dragging over blank area with left button, rotate
|
||||
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
|
||||
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.);
|
||||
if (this->m_canvas_type == ECanvasType::CanvasAssembleView) {
|
||||
if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports ||
|
||||
m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) {
|
||||
//BBS rotate around target
|
||||
Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Vec3d rotate_target = Vec3d::Zero();
|
||||
|
@ -4181,13 +4200,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
camera.recover_from_free_camera();
|
||||
//BBS modify rotation
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::FdmSupports
|
||||
|| m_gizmos.get_current_type() == GLGizmosManager::Seam
|
||||
|| m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) {
|
||||
//camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target);
|
||||
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target);
|
||||
}
|
||||
else if (evt.ControlDown() || evt.CmdDown()) {
|
||||
//if (m_gizmos.get_current_type() == GLGizmosManager::FdmSupports
|
||||
// || m_gizmos.get_current_type() == GLGizmosManager::Seam
|
||||
// || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) {
|
||||
// //camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target);
|
||||
// //camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target);
|
||||
//}
|
||||
//else
|
||||
if (evt.ControlDown() || evt.CmdDown()) {
|
||||
if ((m_rotation_center.x() == 0.f) && (m_rotation_center.y() == 0.f) && (m_rotation_center.z() == 0.f)) {
|
||||
auto canvas_w = float(get_canvas_size().get_width());
|
||||
auto canvas_h = float(get_canvas_size().get_height());
|
||||
|
@ -4280,7 +4300,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
wxGetApp().plater()->select_plate_by_hover_id(hover_idx);
|
||||
//wxGetApp().plater()->get_partplate_list().select_plate_view();
|
||||
//deselect all the objects
|
||||
if (m_hover_volume_idxs.empty())
|
||||
if (m_gizmos.get_current_type() != GLGizmosManager::MeshBoolean && m_hover_volume_idxs.empty())
|
||||
deselect_all();
|
||||
}
|
||||
else if (evt.RightUp() && !is_layers_editing_enabled()) {
|
||||
|
@ -5269,13 +5289,13 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa
|
|||
|
||||
if (imgui->checkbox(_L("Enable rotation"), settings.enable_rotation)) {
|
||||
settings_out.enable_rotation = settings.enable_rotation;
|
||||
appcfg->set("orient", rot_key, settings_out.enable_rotation);
|
||||
appcfg->set("orient", rot_key, settings_out.enable_rotation ? "1" : "0");
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
if (imgui->checkbox(_L("Optimize support interface area"), settings.min_area)) {
|
||||
settings_out.min_area = settings.min_area;
|
||||
appcfg->set("orient", key_min_area, settings_out.min_area);
|
||||
appcfg->set("orient", key_min_area, settings_out.min_area ? "1" : "0");
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
|
@ -5292,8 +5312,8 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa
|
|||
settings_out = OrientSettings{};
|
||||
settings_out.overhang_angle = 60.f;
|
||||
appcfg->set("orient", angle_key, std::to_string(settings_out.overhang_angle));
|
||||
appcfg->set("orient", rot_key, settings_out.enable_rotation );
|
||||
appcfg->set("orient", key_min_area, settings_out.min_area);
|
||||
appcfg->set("orient", rot_key, settings_out.enable_rotation ? "1" : "0");
|
||||
appcfg->set("orient", key_min_area, settings_out.min_area? "1" : "0");
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
|
@ -5644,7 +5664,8 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||
curr_color[2] = vol->color[2];
|
||||
curr_color[3] = vol->color[3];
|
||||
|
||||
shader->set_uniform("uniform_color", curr_color);
|
||||
std::array<float, 4> new_color = adjust_color_for_rendering(curr_color);
|
||||
shader->set_uniform("uniform_color", new_color);
|
||||
shader->set_uniform("volume_world_matrix", vol->world_matrix());
|
||||
//BBS set all volume to orange
|
||||
//shader->set_uniform("uniform_color", orange);
|
||||
|
@ -5683,6 +5704,8 @@ void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, uns
|
|||
return;
|
||||
|
||||
bool multisample = OpenGLManager::can_multisample();
|
||||
if (for_picking)
|
||||
multisample = false;
|
||||
//if (!multisample)
|
||||
// glsafe(::glEnable(GL_MULTISAMPLE));
|
||||
|
||||
|
@ -5789,6 +5812,8 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data,
|
|||
return;
|
||||
|
||||
bool multisample = OpenGLManager::can_multisample();
|
||||
if (for_picking)
|
||||
multisample = false;
|
||||
//if (!multisample)
|
||||
// glsafe(::glEnable(GL_MULTISAMPLE));
|
||||
|
||||
|
@ -6068,6 +6093,8 @@ bool GLCanvas3D::_init_main_toolbar()
|
|||
wxGetApp().plater()->orient();
|
||||
//BBS do not show orient menu
|
||||
//_render_orient_menu(left, right, bottom, top);
|
||||
NetworkAgent* agent = GUI::wxGetApp().getAgent();
|
||||
if (agent) agent->track_update_property("auto_orient", std::to_string(++auto_orient_count));
|
||||
}
|
||||
};
|
||||
if (!m_main_toolbar.add_item(item))
|
||||
|
@ -6664,9 +6691,9 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
|||
//m_bed.render_for_picking(*this, bottom, scale_factor);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id) const
|
||||
void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) const
|
||||
{
|
||||
wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id);
|
||||
wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id, render_cali);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_plates_for_picking() const
|
||||
|
@ -6739,7 +6766,10 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
|||
else {
|
||||
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
|
||||
}
|
||||
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances());
|
||||
if (m_canvas_type == CanvasAssembleView)
|
||||
m_volumes.set_show_sinking_contours(false);
|
||||
else
|
||||
m_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances());
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
||||
ECanvasType canvas_type = this->m_canvas_type;
|
||||
|
@ -7056,8 +7086,9 @@ void GLCanvas3D::_render_overlays()
|
|||
if (m_layers_editing.last_object_id >= 0 && m_layers_editing.object_max_z() > 0.0f)
|
||||
m_layers_editing.render_overlay(*this);
|
||||
|
||||
const ConfigOptionEnum<PrintSequence>* opt = dynamic_cast<const ConfigOptionEnum<PrintSequence>*>(m_config->option<ConfigOptionEnum<PrintSequence>>("print_sequence"));
|
||||
bool sequential_print = opt != nullptr && (opt->value == PrintSequence::ByObject);
|
||||
auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||
auto curr_print_seq = curr_plate->get_real_print_seq();
|
||||
bool sequential_print = (curr_print_seq == PrintSequence::ByObject);
|
||||
std::vector<const ModelInstance*> sorted_instances;
|
||||
if (sequential_print) {
|
||||
const Print* print = fff_print();
|
||||
|
@ -7230,9 +7261,6 @@ void GLCanvas3D::_render_gizmos_overlay()
|
|||
const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale());
|
||||
m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
|
||||
#endif /* __WXMSW__ */
|
||||
if (m_canvas_type == CanvasAssembleView)
|
||||
return;
|
||||
|
||||
m_gizmos.render_overlay();
|
||||
|
||||
if (m_gizmo_highlighter.m_render_arrow)
|
||||
|
@ -7394,7 +7422,6 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, scroll_col);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_active);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_hover);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 10.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
|
@ -7410,7 +7437,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
imgui.begin(_L("Select Plate"), ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse);
|
||||
ImGui::SetWindowFontScale(1.2f);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * f_scale);
|
||||
|
||||
ImVec2 size = ImVec2(button_width, button_height); // Size of the image we want to make visible
|
||||
ImVec4 bg_col = ImVec4(128.0f, 128.0f, 128.0f, 0.0f);
|
||||
|
@ -7435,7 +7462,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_hover);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_hover);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7477,6 +7504,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
ImVec2 size = ImVec2(button_width, button_height * all_plates_stats_item->percent / 100.0f);
|
||||
ImVec2 rect_start_pos = ImVec2(start_pos.x, start_pos.y + size.y);
|
||||
ImVec2 rect_end_pos = ImVec2(start_pos.x + button_width, start_pos.y + button_height);
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, rect_end_pos, IM_COL32(0, 0, 0, 10));
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(rect_start_pos, rect_end_pos, IM_COL32(0, 0, 0, 80));
|
||||
}
|
||||
else if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED) {
|
||||
|
@ -7485,6 +7513,11 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(40, 1, 1, 64));
|
||||
ImGui::GetForegroundDrawList()->AddRect(start_pos, end_pos, IM_COL32(208, 27, 27, 255), 0.0f, 0, 1.0f);
|
||||
}
|
||||
else if (all_plates_stats_item->slice_state == IMToolbarItem::SliceState::SLICED) {
|
||||
ImVec2 size = ImVec2(button_width, button_height);
|
||||
ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y);
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(0, 0, 0, 10));
|
||||
}
|
||||
|
||||
// draw text
|
||||
GImGui->FontSize = 15.0f;
|
||||
|
@ -7508,16 +7541,31 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
ImVec2 uv0 = ImVec2(0.0f, 1.0f); // UV coordinates for lower-left
|
||||
ImVec2 uv1 = ImVec2(1.0f, 0.0f); // UV coordinates in our texture
|
||||
|
||||
auto button_pos = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorPos(button_pos + margin);
|
||||
|
||||
ImGui::Image(item->texture_id, size, uv0, uv1, tint_col);
|
||||
|
||||
ImGui::SetCursorPos(button_pos);
|
||||
|
||||
// invisible button
|
||||
auto button_size = size + margin + margin + ImVec2(2 * frame_padding, 2 * frame_padding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f * f_scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
if (item->selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, button_active);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_active);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, button_active);
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f));
|
||||
if (ImGui::IsMouseHoveringRect(button_pos, button_pos + button_size)) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, button_hover);
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(.0f, .0f, .0f, .0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::ImageButton2(item->texture_id, size, uv0, uv1, frame_padding, bg_col, tint_col, margin)) {
|
||||
if(ImGui::Button("##invisible_button", button_size)){
|
||||
if (m_process && !m_process->running()) {
|
||||
all_plates_stats_item->selected = false;
|
||||
item->selected = true;
|
||||
|
@ -7527,8 +7575,8 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
wxQueueEvent(wxGetApp().plater(), evt);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImVec2 start_pos = ImVec2(button_start_pos.x + frame_padding + margin.x, button_start_pos.y + frame_padding + margin.y);
|
||||
if (item->slice_state == IMToolbarItem::SliceState::UNSLICED) {
|
||||
|
@ -7539,12 +7587,17 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
ImVec2 size = ImVec2(button_width, button_height * item->percent / 100.0f);
|
||||
ImVec2 rect_start_pos = ImVec2(start_pos.x, start_pos.y + size.y);
|
||||
ImVec2 rect_end_pos = ImVec2(start_pos.x + button_width, start_pos.y + button_height);
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, rect_end_pos, IM_COL32(0, 0, 0, 10));
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(rect_start_pos, rect_end_pos, IM_COL32(0, 0, 0, 80));
|
||||
} else if (item->slice_state == IMToolbarItem::SliceState::SLICE_FAILED) {
|
||||
ImVec2 size = ImVec2(button_width, button_height);
|
||||
ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y);
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(40, 1, 1, 64));
|
||||
ImGui::GetForegroundDrawList()->AddRect(start_pos, end_pos, IM_COL32(208, 27, 27, 255), 0.0f, 0, 1.0f);
|
||||
} else if (item->slice_state == IMToolbarItem::SliceState::SLICED) {
|
||||
ImVec2 size = ImVec2(button_width, button_height);
|
||||
ImVec2 end_pos = ImVec2(start_pos.x + size.x, start_pos.y + size.y);
|
||||
ImGui::GetForegroundDrawList()->AddRectFilled(start_pos, end_pos, IM_COL32(0, 0, 0, 10));
|
||||
}
|
||||
|
||||
// draw text
|
||||
|
@ -7554,7 +7607,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
|||
ImGui::PopID();
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(9);
|
||||
ImGui::PopStyleColor(8);
|
||||
ImGui::PopStyleVar(5);
|
||||
|
||||
if (ImGui::IsWindowHovered() || is_hovered) {
|
||||
|
@ -7645,6 +7698,8 @@ void GLCanvas3D::_render_return_toolbar() const
|
|||
if (ImGui::ImageTextButton(real_size,_utf8(L("return")).c_str(), m_return_toolbar.get_return_texture_id(), button_icon_size, uv0, uv1, -1, bg_col, tint_col, margin)) {
|
||||
if (m_canvas != nullptr)
|
||||
wxPostEvent(m_canvas, SimpleEvent(EVT_GLVIEWTOOLBAR_3D));
|
||||
const_cast<GLGizmosManager*>(&m_gizmos)->reset_all_states();
|
||||
wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager().reset_all_states();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
ImGui::PopStyleVar(1);
|
||||
|
@ -7846,6 +7901,7 @@ void GLCanvas3D::_render_paint_toolbar() const
|
|||
ImGui::BBLRenderArrow(draw_list, right_arrow_button.GetCenter() - ImVec2(draw_list->_Data->FontSize, draw_list->_Data->FontSize) * 0.5f, arrow_color, ImGuiDir_Right, 2.0f);
|
||||
}
|
||||
|
||||
m_paint_toolbar_width = (ImGui::GetWindowWidth() + 50.0f * em_unit * f_scale);
|
||||
imgui.end();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
|
@ -7858,6 +7914,10 @@ void GLCanvas3D::_render_assemble_control() const
|
|||
GLVolume::explosion_ratio = m_explosion_ratio = 1.0;
|
||||
return;
|
||||
}
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::EType::MmuSegmentation) {
|
||||
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(0.0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiWrapper* imgui = wxGetApp().imgui();
|
||||
|
||||
|
@ -8845,15 +8905,22 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
|||
{
|
||||
_set_current();
|
||||
bool show = false;
|
||||
if (!m_volumes.empty())
|
||||
if (!m_volumes.empty()) {
|
||||
show = _is_any_volume_outside();
|
||||
else {
|
||||
show &= m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.m_conflict_result.has_value();
|
||||
} else {
|
||||
if (wxGetApp().is_editor()) {
|
||||
if (current_printer_technology() != ptSLA)
|
||||
if (warning == EWarning::ToolpathOutside)
|
||||
show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
|
||||
else if (warning==EWarning::GCodeConflict)
|
||||
if (current_printer_technology() != ptSLA) {
|
||||
unsigned int max_z_layer = m_gcode_viewer.get_layers_z_range().back();
|
||||
if (warning == EWarning::ToolHeightOutside) // check if max z_layer height exceed max print height
|
||||
show = m_gcode_viewer.has_data() && (m_gcode_viewer.get_layers_zs()[max_z_layer] - m_gcode_viewer.get_max_print_height() >= 1e-6);
|
||||
else if (warning == EWarning::ToolpathOutside) { // check if max x,y coords exceed bed area
|
||||
show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed() &&
|
||||
(m_gcode_viewer.get_max_print_height() -m_gcode_viewer.get_layers_zs()[max_z_layer] >= 1e-6);
|
||||
}
|
||||
else if (warning == EWarning::GCodeConflict)
|
||||
show = m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.m_conflict_result.has_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8887,12 +8954,17 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
|||
enum ErrorType{
|
||||
PLATER_WARNING,
|
||||
PLATER_ERROR,
|
||||
SLICING_SERIOUS_WARNING,
|
||||
SLICING_ERROR
|
||||
};
|
||||
std::string text;
|
||||
ErrorType error = ErrorType::PLATER_WARNING;
|
||||
const ModelObject* conflictObj=nullptr;
|
||||
switch (warning) {
|
||||
case EWarning::GCodeConflict: {
|
||||
static std::string prevConflictText;
|
||||
text = prevConflictText;
|
||||
error = ErrorType::SLICING_SERIOUS_WARNING;
|
||||
if (!m_gcode_viewer.m_conflict_result) { break; }
|
||||
std::string objName1 = m_gcode_viewer.m_conflict_result.value()._objName1;
|
||||
std::string objName2 = m_gcode_viewer.m_conflict_result.value()._objName2;
|
||||
|
@ -8901,10 +8973,13 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
|||
text = (boost::format(_u8L("Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please separate the conflicted objects farther (%s <-> %s).")) % layer %
|
||||
height % objName1 % objName2)
|
||||
.str();
|
||||
error = ErrorType::SLICING_ERROR;
|
||||
prevConflictText = text;
|
||||
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(m_gcode_viewer.m_conflict_result.value()._obj2);
|
||||
conflictObj = obj2->model_object();
|
||||
break;
|
||||
}
|
||||
case EWarning::ObjectOutside: text = _u8L("An object is layed over the boundary of plate."); break;
|
||||
case EWarning::ToolHeightOutside: text = _u8L("A G-code path goes beyond the max print height."); error = ErrorType::SLICING_ERROR; break;
|
||||
case EWarning::ToolpathOutside: text = _u8L("A G-code path goes beyond the boundary of plate."); error = ErrorType::SLICING_ERROR; break;
|
||||
// BBS: remove _u8L() for SLA
|
||||
case EWarning::SlaSupportsOutside: text = ("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break;
|
||||
|
@ -8920,25 +8995,6 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
|||
return;
|
||||
auto& notification_manager = *wxGetApp().plater()->get_notification_manager();
|
||||
|
||||
if (warning == EWarning::GCodeConflict && m_gcode_viewer.m_conflict_result) {
|
||||
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(m_gcode_viewer.m_conflict_result.value()._obj2);
|
||||
auto mo = obj2->model_object();
|
||||
ObjectID id = mo->id();
|
||||
auto action_fn = [id](wxEvtHandler *) {
|
||||
auto &objects = wxGetApp().model().objects;
|
||||
auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end();
|
||||
if (iter != objects.end()) {
|
||||
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
|
||||
wxGetApp().obj_list()->select_items({{*iter, nullptr}});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto hypertext = _u8L("Jump to");
|
||||
hypertext += std::string(" [") + mo->name + "]";
|
||||
notification_manager.push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::ErrorNotificationLevel, _u8L("ERROR:") + "\n" + text,
|
||||
hypertext, action_fn);
|
||||
return;
|
||||
}
|
||||
switch (error)
|
||||
{
|
||||
case PLATER_WARNING:
|
||||
|
@ -8953,9 +9009,15 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
|||
else
|
||||
notification_manager.close_plater_error_notification(text);
|
||||
break;
|
||||
case SLICING_SERIOUS_WARNING:
|
||||
if (state)
|
||||
notification_manager.push_slicing_serious_warning_notification(text, {conflictObj});
|
||||
else
|
||||
notification_manager.close_slicing_serious_warning_notification(text);
|
||||
break;
|
||||
case SLICING_ERROR:
|
||||
if (state)
|
||||
notification_manager.push_slicing_error_notification(text, nullptr);
|
||||
notification_manager.push_slicing_error_notification(text, {conflictObj});
|
||||
else
|
||||
notification_manager.close_slicing_error_notification(text);
|
||||
break;
|
||||
|
|
|
@ -373,7 +373,8 @@ class GLCanvas3D
|
|||
SlaSupportsOutside,
|
||||
SomethingNotShown,
|
||||
ObjectClashed,
|
||||
GCodeConflict
|
||||
GCodeConflict,
|
||||
ToolHeightOutside
|
||||
};
|
||||
|
||||
class RenderStats
|
||||
|
@ -439,6 +440,8 @@ class GLCanvas3D
|
|||
bool is_enabled() const { return m_enabled; }
|
||||
void use(bool use) { m_volumes.set_slope_active(m_enabled ? use : false); }
|
||||
bool is_used() const { return m_volumes.is_slope_active(); }
|
||||
void globalUse(bool use) { m_volumes.set_slope_GlobalActive(m_enabled ? use : false); }
|
||||
bool is_GlobalUsed() const { return m_volumes.is_slope_GlobalActive(); }
|
||||
void set_normal_angle(float angle_in_deg) const {
|
||||
m_volumes.set_slope_normal_z(-::cos(Geometry::deg2rad(90.0f - angle_in_deg)));
|
||||
}
|
||||
|
@ -515,6 +518,7 @@ private:
|
|||
mutable IMToolbar m_sel_plate_toolbar;
|
||||
mutable GLToolbar m_assemble_view_toolbar;
|
||||
mutable IMReturnToolbar m_return_toolbar;
|
||||
mutable float m_paint_toolbar_width;
|
||||
|
||||
//BBS: add canvas type for assemble view usage
|
||||
ECanvasType m_canvas_type;
|
||||
|
@ -619,6 +623,15 @@ private:
|
|||
|
||||
ArrangeSettings &get_arrange_settings() { return get_arrange_settings(this); }
|
||||
|
||||
|
||||
//BBS:record key botton frequency
|
||||
int auto_orient_count = 0;
|
||||
int auto_arrange_count = 0;
|
||||
int split_to_objects_count = 0;
|
||||
int split_to_part_count = 0;
|
||||
int custom_height_count = 0;
|
||||
int custom_painting_count = 0;
|
||||
|
||||
public:
|
||||
OrientSettings& get_orient_settings()
|
||||
{
|
||||
|
@ -707,6 +720,7 @@ public:
|
|||
bool init();
|
||||
void post_event(wxEvent &&event);
|
||||
|
||||
void reset_explosion_ratio() { m_explosion_ratio = 1.0; }
|
||||
void on_change_color_mode(bool is_dark, bool reinit = true);
|
||||
const bool get_dark_mode_status() { return m_is_dark; }
|
||||
void set_as_dirty();
|
||||
|
@ -716,6 +730,7 @@ public:
|
|||
const GLVolumeCollection& get_volumes() const { return m_volumes; }
|
||||
void reset_volumes();
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
||||
bool is_all_plates_selected() { return m_sel_plate_toolbar.m_all_plates_stats_item && m_sel_plate_toolbar.m_all_plates_stats_item->selected; }
|
||||
const float get_scale() const;
|
||||
|
||||
//BBS
|
||||
|
@ -813,6 +828,7 @@ public:
|
|||
float get_main_toolbar_width() { return m_main_toolbar.get_width();}
|
||||
float get_assemble_view_toolbar_width() { return m_assemble_view_toolbar.get_width(); }
|
||||
float get_assemble_view_toolbar_height() { return m_assemble_view_toolbar.get_height(); }
|
||||
float get_assembly_paint_toolbar_width() { return m_paint_toolbar_width; }
|
||||
float get_separator_toolbar_width() { return m_separator_toolbar.get_width(); }
|
||||
float get_separator_toolbar_height() { return m_separator_toolbar.get_height(); }
|
||||
float get_collapse_toolbar_width();
|
||||
|
@ -992,6 +1008,9 @@ public:
|
|||
bool are_labels_shown() const { return m_labels.is_shown(); }
|
||||
void show_labels(bool show) { m_labels.show(show); }
|
||||
|
||||
bool is_overhang_shown() const { return m_slope.is_GlobalUsed(); }
|
||||
void show_overhang(bool show) { m_slope.globalUse(show); }
|
||||
|
||||
bool is_using_slope() const { return m_slope.is_used(); }
|
||||
void use_slope(bool use) { m_slope.use(use); }
|
||||
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
|
||||
|
@ -1093,7 +1112,7 @@ private:
|
|||
void _render_bed(bool bottom, bool show_axes);
|
||||
void _render_bed_for_picking(bool bottom);
|
||||
//BBS: add part plate related logic
|
||||
void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1) const;
|
||||
void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false) const;
|
||||
void _render_plates_for_picking() const;
|
||||
//BBS: add outline drawing logic
|
||||
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
|
||||
|
|
|
@ -3545,6 +3545,18 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
|
|||
}
|
||||
}
|
||||
|
||||
void ObjectList::add_objects_to_list(std::vector<size_t> obj_idxs, bool call_selection_changed, bool notify_partplate, bool do_info_update)
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
AssociateModel(nullptr);
|
||||
#endif
|
||||
for (const size_t idx : obj_idxs) {
|
||||
add_object_to_list(idx, call_selection_changed, notify_partplate, do_info_update);
|
||||
}
|
||||
#ifdef __WXOSX__
|
||||
AssociateModel(m_objects_model);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed, bool notify_partplate, bool do_info_update)
|
||||
|
|
|
@ -325,6 +325,8 @@ public:
|
|||
void part_selection_changed();
|
||||
|
||||
// Add object to the list
|
||||
// @param do_info_update: [Arthur] this function becomes slow as more functions are added, but I only need a fast version in FillBedJob, and I don't care about any info updates, so I pass a do_info_update param to skip all the uneccessary steps.
|
||||
void add_objects_to_list(std::vector<size_t> obj_idxs, bool call_selection_changed = true, bool notify_partplate = true, bool do_info_update = true);
|
||||
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true, bool notify_partplate = true, bool do_info_update = true);
|
||||
// Add object's volumes to the list
|
||||
// Return selected items, if add_to_selection is defined
|
||||
|
|
|
@ -1297,7 +1297,7 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b
|
|||
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
|
||||
unsigned int current_active_id = ImGui::GetActiveID();
|
||||
|
||||
Vec3d rotation = {Geometry::rad2deg(m_rotation(0)), Geometry::rad2deg(m_rotation(1)), Geometry::rad2deg(m_rotation(2))};
|
||||
Vec3d rotation = {Geometry::rad2deg(m_buffered_rotation(0)), Geometry::rad2deg(m_buffered_rotation(1)), Geometry::rad2deg(m_buffered_rotation(2))};
|
||||
char buf[3][64];
|
||||
float buf_size[3];
|
||||
float vec_max = 0, unit_size = 0;
|
||||
|
@ -1450,6 +1450,10 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b
|
|||
|
||||
m_imgui->disabled_begin(has_connectors);
|
||||
m_imgui->bbl_checkbox(_L("Cut to parts"), m_cut_to_parts);
|
||||
if (m_cut_to_parts) {
|
||||
m_keep_upper = true;
|
||||
m_keep_lower = true;
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
|
||||
#if 0
|
||||
|
@ -1585,7 +1589,9 @@ void GLGizmoAdvancedCut::render_connectors_input_window(float x, float y, float
|
|||
if (render_combo(_u8L("Style"), connector_styles, m_connector_style))
|
||||
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
|
||||
m_imgui->disabled_end();
|
||||
ImGuiWrapper::pop_combo_style();
|
||||
|
||||
ImGuiWrapper::push_combo_style(m_parent.get_scale());
|
||||
if (render_combo(_u8L("Shape"), connector_shapes, m_connector_shape_id))
|
||||
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
|
||||
ImGuiWrapper::pop_combo_style();
|
||||
|
@ -1728,11 +1734,13 @@ bool GLGizmoAdvancedCut::render_combo(const std::string &label, const std::vecto
|
|||
|
||||
bool GLGizmoAdvancedCut::render_slider_double_input(const std::string &label, float &value_in, float &tolerance_in)
|
||||
{
|
||||
// -------- [ ] -------- [ ]
|
||||
// slider_with + item_in_gap + first_input_width + item_out_gap + slider_with + item_in_gap + second_input_width
|
||||
double slider_with = 0.24 * m_editing_window_width; // m_control_width * 0.35;
|
||||
double item_in_gap = 0.01 * m_editing_window_width;
|
||||
double item_out_gap = 0.02 * m_editing_window_width;
|
||||
double first_input_width = 0.33 * m_editing_window_width;
|
||||
double second_input_width = 0.15 * m_editing_window_width;
|
||||
double item_out_gap = 0.01 * m_editing_window_width;
|
||||
double first_input_width = 0.29 * m_editing_window_width;
|
||||
double second_input_width = 0.29 * m_editing_window_width;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(label);
|
||||
|
@ -1770,18 +1778,21 @@ bool GLGizmoAdvancedCut::render_slider_double_input(const std::string &label, fl
|
|||
ImGui::SameLine(left_width);
|
||||
ImGui::PushItemWidth(slider_with);
|
||||
|
||||
float old_tolerance, tolerance = old_tolerance = tolerance_in * 100.f;
|
||||
std::string format_t = tolerance_in < 0.f ? " " : "%.f %%";
|
||||
float tolerance = tolerance_in;
|
||||
if (m_imperial_units)
|
||||
tolerance *= float(units_mm_to_in);
|
||||
float old_tolerance = tolerance;
|
||||
//std::string format_t = tolerance_in < 0.f ? " " : "%.f %%";
|
||||
float min_tolerance = tolerance_in < 0.f ? UndefMinVal : 0.f;
|
||||
|
||||
m_imgui->bbl_slider_float_style(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, 20.f, format_t.c_str(), 1.f, true, _L("Tolerance"));
|
||||
m_imgui->bbl_slider_float_style(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, 2.f, format.c_str(), 1.f, true, _L("Tolerance"));
|
||||
|
||||
left_width += (slider_with + item_in_gap);
|
||||
ImGui::SameLine(left_width);
|
||||
ImGui::PushItemWidth(second_input_width);
|
||||
ImGui::BBLDragFloat(("##tolerance_input_" + label).c_str(), &tolerance, 0.05f, min_tolerance, 20.f, format_t.c_str());
|
||||
ImGui::BBLDragFloat(("##tolerance_input_" + label).c_str(), &tolerance, 0.05f, min_tolerance, 2.f, format.c_str());
|
||||
|
||||
tolerance_in = tolerance * 0.01f;
|
||||
tolerance_in = tolerance * float(m_imperial_units ? units_in_to_mm : 1.0);
|
||||
|
||||
return !is_approx(old_val, value) || !is_approx(old_tolerance, tolerance);
|
||||
}
|
||||
|
|
|
@ -191,6 +191,9 @@ public:
|
|||
|
||||
virtual std::string get_tooltip() const { return ""; }
|
||||
|
||||
int get_count() { return ++count; }
|
||||
std::string get_gizmo_name() { return on_get_name(); }
|
||||
|
||||
protected:
|
||||
float last_input_window_width = 0;
|
||||
virtual bool on_init() = 0;
|
||||
|
@ -229,6 +232,7 @@ private:
|
|||
// Flag for dirty visible state of Gizmo
|
||||
// When True then need new rendering
|
||||
bool m_dirty;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
|
|||
shader->set_uniform("volume_world_matrix", trafo_matrix);
|
||||
shader->set_uniform("volume_mirrored", is_left_handed);
|
||||
shader->set_uniform("slope.actived", m_parent.is_using_slope());
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix);
|
||||
shader->set_uniform("slope.normal_z", normal_z);
|
||||
m_triangle_selectors[mesh_id]->render(m_imgui);
|
||||
|
||||
|
@ -290,7 +290,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
|
||||
}
|
||||
|
|
|
@ -141,6 +141,7 @@ void GLGizmoFlatten::update_planes()
|
|||
// Following constants are used for discarding too small polygons.
|
||||
const float minimal_area = 5.f; // in square mm (world coordinates)
|
||||
const float minimal_side = 1.f; // mm
|
||||
const float minimal_angle = 1.f; // degree, initial value was 10, but cause bugs
|
||||
|
||||
// Now we'll go through all the facets and append Points of facets sharing the same normal.
|
||||
// This part is still performed in mesh coordinate system.
|
||||
|
@ -151,9 +152,9 @@ void GLGizmoFlatten::update_planes()
|
|||
std::vector<bool> facet_visited(num_of_facets, false);
|
||||
int facet_queue_cnt = 0;
|
||||
const stl_normal* normal_ptr = nullptr;
|
||||
int facet_idx = 0;
|
||||
while (1) {
|
||||
// Find next unvisited triangle:
|
||||
int facet_idx = 0;
|
||||
for (; facet_idx < num_of_facets; ++ facet_idx)
|
||||
if (!facet_visited[facet_idx]) {
|
||||
facet_queue[facet_queue_cnt ++] = facet_idx;
|
||||
|
@ -235,7 +236,7 @@ void GLGizmoFlatten::update_planes()
|
|||
discard = true;
|
||||
else {
|
||||
// We also check the inner angles and discard polygons with angles smaller than the following threshold
|
||||
const double angle_threshold = ::cos(10.0 * (double)PI / 180.0);
|
||||
const double angle_threshold = ::cos(minimal_angle * (double)PI / 180.0);
|
||||
|
||||
for (unsigned int i = 0; i < polygon.size(); ++i) {
|
||||
const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1];
|
||||
|
@ -250,7 +251,8 @@ void GLGizmoFlatten::update_planes()
|
|||
}
|
||||
|
||||
if (discard) {
|
||||
m_planes.erase(m_planes.begin() + (polygon_id--));
|
||||
m_planes[polygon_id--] = std::move(m_planes.back());
|
||||
m_planes.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
466
src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp
Normal file
466
src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp
Normal file
|
@ -0,0 +1,466 @@
|
|||
#include "GLGizmoMeshBoolean.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "libslic3r/MeshBoolean.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include <imgui/imgui_internal.h>
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static const std::string warning_text = _u8L("Unable to perform boolean operation on selected parts");
|
||||
|
||||
GLGizmoMeshBoolean::GLGizmoMeshBoolean(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
||||
: GLGizmoBase(parent, icon_filename, sprite_id)
|
||||
{
|
||||
}
|
||||
|
||||
GLGizmoMeshBoolean::~GLGizmoMeshBoolean()
|
||||
{
|
||||
}
|
||||
|
||||
bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
|
||||
{
|
||||
if (action == SLAGizmoEventType::LeftDown) {
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
if (mo == nullptr)
|
||||
return true;
|
||||
const ModelInstance* mi = mo->instances[m_parent.get_selection().get_instance_idx()];
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
//if (mv->is_model_part()) {
|
||||
trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix());
|
||||
//}
|
||||
}
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Vec3f normal = Vec3f::Zero();
|
||||
Vec3f hit = Vec3f::Zero();
|
||||
size_t facet = 0;
|
||||
Vec3f closest_hit = Vec3f::Zero();
|
||||
Vec3f closest_normal = Vec3f::Zero();
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
int closest_hit_mesh_id = -1;
|
||||
|
||||
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
|
||||
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
|
||||
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh());
|
||||
if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal,
|
||||
m_c->object_clipper()->get_clipping_plane(), &facet)) {
|
||||
// Is this hit the closest to the camera so far?
|
||||
double hit_squared_distance = (camera.get_position() - trafo_matrices[mesh_id] * hit.cast<double>()).squaredNorm();
|
||||
if (hit_squared_distance < closest_hit_squared_distance) {
|
||||
closest_hit_squared_distance = hit_squared_distance;
|
||||
closest_hit_mesh_id = mesh_id;
|
||||
closest_hit = hit;
|
||||
closest_normal = normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_hit == Vec3f::Zero() && closest_normal == Vec3f::Zero())
|
||||
return true;
|
||||
|
||||
if (get_selecting_state() == MeshBooleanSelectingState::SelectTool) {
|
||||
m_tool.trafo = trafo_matrices[closest_hit_mesh_id];
|
||||
m_tool.volume_idx = closest_hit_mesh_id;
|
||||
set_tool_volume(mo->volumes[closest_hit_mesh_id]);
|
||||
return true;
|
||||
}
|
||||
if (get_selecting_state() == MeshBooleanSelectingState::SelectSource) {
|
||||
m_src.trafo = trafo_matrices[closest_hit_mesh_id];
|
||||
m_src.volume_idx = closest_hit_mesh_id;
|
||||
set_src_volume(mo->volumes[closest_hit_mesh_id]);
|
||||
m_selecting_state = MeshBooleanSelectingState::SelectTool;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLGizmoMeshBoolean::on_init()
|
||||
{
|
||||
m_shortcut_key = WXK_CONTROL_B;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GLGizmoMeshBoolean::on_get_name() const
|
||||
{
|
||||
return _u8L("Mesh Boolean");
|
||||
}
|
||||
|
||||
bool GLGizmoMeshBoolean::on_is_activable() const
|
||||
{
|
||||
return m_parent.get_selection().is_single_full_instance() && m_parent.get_selection().get_volume_idxs().size() > 1;
|
||||
}
|
||||
|
||||
void GLGizmoMeshBoolean::on_render()
|
||||
{
|
||||
if (m_parent.get_selection().get_object_idx() < 0)
|
||||
return;
|
||||
static ModelObject* last_mo = nullptr;
|
||||
ModelObject* curr_mo = m_parent.get_selection().get_model()->objects[m_parent.get_selection().get_object_idx()];
|
||||
if (last_mo != curr_mo) {
|
||||
last_mo = curr_mo;
|
||||
m_src.reset();
|
||||
m_tool.reset();
|
||||
m_operation_mode = MeshBooleanOperation::Union;
|
||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||
return;
|
||||
}
|
||||
|
||||
BoundingBoxf3 src_bb;
|
||||
BoundingBoxf3 tool_bb;
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance* mi = mo->instances[m_parent.get_selection().get_instance_idx()];
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||
for (unsigned int i : idxs) {
|
||||
const GLVolume* volume = selection.get_volume(i);
|
||||
if(volume->volume_idx() == m_src.volume_idx) {
|
||||
src_bb = volume->transformed_convex_hull_bounding_box();
|
||||
}
|
||||
if (volume->volume_idx() == m_tool.volume_idx) {
|
||||
tool_bb = volume->transformed_convex_hull_bounding_box();
|
||||
}
|
||||
}
|
||||
|
||||
float src_color[3] = { 1.0f, 1.0f, 1.0f };
|
||||
float tool_color[3] = { 0.0f, 174.0f / 255.0f, 66.0f / 255.0f };
|
||||
m_parent.get_selection().render_bounding_box(src_bb, src_color, m_parent.get_scale());
|
||||
m_parent.get_selection().render_bounding_box(tool_bb, tool_color, m_parent.get_scale());
|
||||
}
|
||||
|
||||
void GLGizmoMeshBoolean::on_set_state()
|
||||
{
|
||||
if (m_state == EState::On) {
|
||||
m_src.reset();
|
||||
m_tool.reset();
|
||||
bool m_diff_delete_input = false;
|
||||
bool m_inter_delete_input = false;
|
||||
m_operation_mode = MeshBooleanOperation::Union;
|
||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||
}
|
||||
else if (m_state == EState::Off) {
|
||||
m_src.reset();
|
||||
m_tool.reset();
|
||||
bool m_diff_delete_input = false;
|
||||
bool m_inter_delete_input = false;
|
||||
m_operation_mode = MeshBooleanOperation::Undef;
|
||||
m_selecting_state = MeshBooleanSelectingState::Undef;
|
||||
wxGetApp().notification_manager()->close_plater_warning_notification(warning_text);
|
||||
}
|
||||
}
|
||||
|
||||
CommonGizmosDataID GLGizmoMeshBoolean::on_get_requirements() const
|
||||
{
|
||||
return CommonGizmosDataID(
|
||||
int(CommonGizmosDataID::SelectionInfo)
|
||||
| int(CommonGizmosDataID::InstancesHider)
|
||||
| int(CommonGizmosDataID::Raycaster)
|
||||
| int(CommonGizmosDataID::ObjectClipper));
|
||||
}
|
||||
|
||||
void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
y = std::min(y, bottom_limit - ImGui::GetWindowHeight());
|
||||
|
||||
static float last_y = 0.0f;
|
||||
static float last_w = 0.0f;
|
||||
|
||||
const float currt_scale = m_parent.get_scale();
|
||||
ImGuiWrapper::push_toolbar_style(currt_scale);
|
||||
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
|
||||
GizmoImguiBegin("MeshBoolean", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
const int max_tab_length = 2 * ImGui::GetStyle().FramePadding.x + std::max(ImGui::CalcTextSize(_L("Union").c_str()).x,
|
||||
std::max(ImGui::CalcTextSize(_L("Difference").c_str()).x, ImGui::CalcTextSize(_L("Intersection").c_str()).x));
|
||||
const int max_cap_length = ImGui::GetStyle().WindowPadding.x + ImGui::GetStyle().ItemSpacing.x + std::max(ImGui::CalcTextSize(_L("Source Volume").c_str()).x, ImGui::CalcTextSize(_L("Tool Volume").c_str()).x);
|
||||
const int select_btn_length = 2 * ImGui::GetStyle().FramePadding.x + std::max(ImGui::CalcTextSize(("1 " + _L("selected")).c_str()).x, ImGui::CalcTextSize(_L("Select").c_str()).x);
|
||||
|
||||
auto selectable = [this](const wxString& label, bool selected, const ImVec2& size_arg) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0,0 });
|
||||
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
const ImVec2 label_size = ImGui::CalcTextSize(label.c_str(), NULL, true);
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + ImGui::GetStyle().FramePadding.x * 2.0f, label_size.y + ImGui::GetStyle().FramePadding.y * 2.0f);
|
||||
bool hovered = ImGui::IsMouseHoveringRect(pos, pos + size);
|
||||
|
||||
if (selected || hovered) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f });
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f });
|
||||
}
|
||||
|
||||
bool res = ImGui::Button(label.c_str(), size_arg);
|
||||
|
||||
if (selected || hovered) {
|
||||
ImGui::PopStyleColor(4);
|
||||
}
|
||||
else {
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(1);
|
||||
return res;
|
||||
};
|
||||
|
||||
auto operate_button = [this](const wxString &label, bool enable) {
|
||||
if (!enable) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
if (m_is_dark_mode) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(39.0f / 255.0f, 39.0f / 255.0f, 39.0f / 255.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(108.0f / 255.0f, 108.0f / 255.0f, 108.0f / 255.0f, 1.0f));
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(163.0f / 255.0f, 163.0f / 255.0f, 163.0f / 255.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
bool res = m_imgui->button(label.c_str());
|
||||
|
||||
if (!enable) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
|
||||
if (selectable(_L("Union").c_str(), m_operation_mode == MeshBooleanOperation::Union, ImVec2(max_tab_length, 0.0f))) {
|
||||
m_operation_mode = MeshBooleanOperation::Union;
|
||||
}
|
||||
ImGui::SameLine(0, 0);
|
||||
if (selectable(_L("Difference").c_str(), m_operation_mode == MeshBooleanOperation::Difference, ImVec2(max_tab_length, 0.0f))) {
|
||||
m_operation_mode = MeshBooleanOperation::Difference;
|
||||
}
|
||||
ImGui::SameLine(0, 0);
|
||||
if (selectable(_L("Intersection").c_str(), m_operation_mode == MeshBooleanOperation::Intersection, ImVec2(max_tab_length, 0.0f))) {
|
||||
m_operation_mode = MeshBooleanOperation::Intersection;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
std::string cap_str1 = m_operation_mode != MeshBooleanOperation::Difference ? _u8L("Part 1") : _u8L("Subtract from");
|
||||
m_imgui->text(cap_str1);
|
||||
ImGui::SameLine(max_cap_length);
|
||||
wxString select_src_str = m_src.mv ? "1 " + _L("selected") : _L("Select");
|
||||
select_src_str << "##select_source_volume";
|
||||
ImGui::PushItemWidth(select_btn_length);
|
||||
if (selectable(select_src_str.c_str(), m_selecting_state == MeshBooleanSelectingState::SelectSource, ImVec2(select_btn_length, 0)))
|
||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||
ImGui::PopItemWidth();
|
||||
if (m_src.mv) {
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_src.mv->name);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, { 0, 0, 0, 0 });
|
||||
if (ImGui::Button((into_u8(ImGui::TextSearchCloseIcon) + "##src").c_str(), {18, 18}))
|
||||
{
|
||||
m_src.reset();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
std::string cap_str2 = m_operation_mode != MeshBooleanOperation::Difference ? _u8L("Part 2") : _u8L("Subtract with");
|
||||
m_imgui->text(cap_str2);
|
||||
ImGui::SameLine(max_cap_length);
|
||||
wxString select_tool_str = m_tool.mv ? "1 " + _L("selected") : _L("Select");
|
||||
select_tool_str << "##select_tool_volume";
|
||||
ImGui::PushItemWidth(select_btn_length);
|
||||
if (selectable(select_tool_str.c_str(), m_selecting_state == MeshBooleanSelectingState::SelectTool, ImVec2(select_btn_length, 0)))
|
||||
m_selecting_state = MeshBooleanSelectingState::SelectTool;
|
||||
ImGui::PopItemWidth();
|
||||
if (m_tool.mv) {
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_tool.mv->name);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, { 0, 0, 0, 0 });
|
||||
if (ImGui::Button((into_u8(ImGui::TextSearchCloseIcon) + "tool").c_str(), {18, 18}))
|
||||
{
|
||||
m_tool.reset();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
|
||||
bool enable_button = m_src.mv && m_tool.mv;
|
||||
if (m_operation_mode == MeshBooleanOperation::Union)
|
||||
{
|
||||
if (operate_button(_L("Union") + "##btn", enable_button)) {
|
||||
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
||||
temp_src_mesh.transform(m_src.trafo);
|
||||
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
||||
temp_tool_mesh.transform(m_tool.trafo);
|
||||
std::vector<TriangleMesh> temp_mesh_resuls;
|
||||
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION");
|
||||
if (temp_mesh_resuls.size() != 0) {
|
||||
generate_new_volume(true, *temp_mesh_resuls.begin());
|
||||
wxGetApp().notification_manager()->close_plater_warning_notification(warning_text);
|
||||
}
|
||||
else {
|
||||
wxGetApp().notification_manager()->push_plater_warning_notification(warning_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_operation_mode == MeshBooleanOperation::Difference) {
|
||||
m_imgui->bbl_checkbox(_L("Delete input"), m_diff_delete_input);
|
||||
if (operate_button(_L("Difference") + "##btn", enable_button)) {
|
||||
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
||||
temp_src_mesh.transform(m_src.trafo);
|
||||
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
||||
temp_tool_mesh.transform(m_tool.trafo);
|
||||
std::vector<TriangleMesh> temp_mesh_resuls;
|
||||
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B");
|
||||
if (temp_mesh_resuls.size() != 0) {
|
||||
generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin());
|
||||
wxGetApp().notification_manager()->close_plater_warning_notification(warning_text);
|
||||
}
|
||||
else {
|
||||
wxGetApp().notification_manager()->push_plater_warning_notification(warning_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_operation_mode == MeshBooleanOperation::Intersection){
|
||||
m_imgui->bbl_checkbox(_L("Delete input"), m_inter_delete_input);
|
||||
if (operate_button(_L("Intersection") + "##btn", enable_button)) {
|
||||
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
||||
temp_src_mesh.transform(m_src.trafo);
|
||||
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
||||
temp_tool_mesh.transform(m_tool.trafo);
|
||||
std::vector<TriangleMesh> temp_mesh_resuls;
|
||||
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION");
|
||||
if (temp_mesh_resuls.size() != 0) {
|
||||
generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin());
|
||||
wxGetApp().notification_manager()->close_plater_warning_notification(warning_text);
|
||||
}
|
||||
else {
|
||||
wxGetApp().notification_manager()->push_plater_warning_notification(warning_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float win_w = ImGui::GetWindowWidth();
|
||||
if (last_w != win_w || last_y != y) {
|
||||
// ask canvas for another frame to render the window in the correct position
|
||||
m_imgui->set_requires_extra_frame();
|
||||
m_parent.set_as_dirty();
|
||||
m_parent.request_extra_frame();
|
||||
if (last_w != win_w)
|
||||
last_w = win_w;
|
||||
if (last_y != y)
|
||||
last_y = y;
|
||||
}
|
||||
|
||||
GizmoImguiEnd();
|
||||
ImGuiWrapper::pop_toolbar_style();
|
||||
}
|
||||
|
||||
void GLGizmoMeshBoolean::on_load(cereal::BinaryInputArchive &ar)
|
||||
{
|
||||
ar(m_enable, m_operation_mode, m_selecting_state, m_diff_delete_input, m_inter_delete_input, m_src, m_tool);
|
||||
ModelObject *curr_model_object = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr;
|
||||
m_src.mv = curr_model_object == nullptr ? nullptr : m_src.volume_idx < 0 ? nullptr : curr_model_object->volumes[m_src.volume_idx];
|
||||
m_tool.mv = curr_model_object == nullptr ? nullptr : m_tool.volume_idx < 0 ? nullptr : curr_model_object->volumes[m_tool.volume_idx];
|
||||
}
|
||||
|
||||
void GLGizmoMeshBoolean::on_save(cereal::BinaryOutputArchive &ar) const
|
||||
{
|
||||
ar(m_enable, m_operation_mode, m_selecting_state, m_diff_delete_input, m_inter_delete_input, m_src, m_tool);
|
||||
}
|
||||
|
||||
void GLGizmoMeshBoolean::generate_new_volume(bool delete_input, const TriangleMesh& mesh_result) {
|
||||
|
||||
wxGetApp().plater()->take_snapshot("Mesh Boolean");
|
||||
|
||||
ModelObject* curr_model_object = m_c->selection_info()->model_object();
|
||||
|
||||
// generate new volume
|
||||
ModelVolume* new_volume = curr_model_object->add_volume(std::move(mesh_result));
|
||||
|
||||
// assign to new_volume from old_volume
|
||||
ModelVolume* old_volume = m_src.mv;
|
||||
std::string suffix;
|
||||
switch (m_operation_mode)
|
||||
{
|
||||
case MeshBooleanOperation::Union:
|
||||
suffix = "union";
|
||||
break;
|
||||
case MeshBooleanOperation::Difference:
|
||||
suffix = "difference";
|
||||
break;
|
||||
case MeshBooleanOperation::Intersection:
|
||||
suffix = "intersection";
|
||||
break;
|
||||
}
|
||||
new_volume->name = old_volume->name + " - " + suffix;
|
||||
new_volume->set_new_unique_id();
|
||||
new_volume->config.apply(old_volume->config);
|
||||
new_volume->set_type(old_volume->type());
|
||||
new_volume->set_material_id(old_volume->material_id());
|
||||
new_volume->set_transformation(old_volume->get_transformation());
|
||||
//Vec3d translate_z = { 0,0, (new_volume->source.mesh_offset - old_volume->source.mesh_offset).z() };
|
||||
//new_volume->translate(new_volume->get_transformation().get_matrix(true) * translate_z);
|
||||
//new_volume->supported_facets.assign(old_volume->supported_facets);
|
||||
//new_volume->seam_facets.assign(old_volume->seam_facets);
|
||||
//new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
|
||||
|
||||
// delete old_volume
|
||||
std::swap(curr_model_object->volumes[m_src.volume_idx], curr_model_object->volumes.back());
|
||||
curr_model_object->delete_volume(curr_model_object->volumes.size() - 1);
|
||||
|
||||
if (delete_input) {
|
||||
std::vector<ItemForDelete> items;
|
||||
auto obj_idx = m_parent.get_selection().get_object_idx();
|
||||
items.emplace_back(ItemType::itVolume, obj_idx, m_tool.volume_idx);
|
||||
wxGetApp().obj_list()->delete_from_model_and_list(items);
|
||||
}
|
||||
|
||||
//bool sinking = curr_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD;
|
||||
//if (!sinking)
|
||||
// curr_model_object->ensure_on_bed();
|
||||
//curr_model_object->sort_volumes(true);
|
||||
|
||||
wxGetApp().plater()->update();
|
||||
wxGetApp().obj_list()->select_item([this, new_volume]() {
|
||||
wxDataViewItem sel_item;
|
||||
|
||||
wxDataViewItemArray items = wxGetApp().obj_list()->reorder_volumes_and_get_selection(m_parent.get_selection().get_object_idx(), [new_volume](const ModelVolume* volume) { return volume == new_volume; });
|
||||
if (!items.IsEmpty())
|
||||
sel_item = items.front();
|
||||
|
||||
return sel_item;
|
||||
});
|
||||
|
||||
m_src.reset();
|
||||
m_tool.reset();
|
||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||
}
|
||||
|
||||
|
||||
}}
|
88
src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp
Normal file
88
src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#ifndef slic3r_GLGizmoMeshBoolean_hpp_
|
||||
#define slic3r_GLGizmoMeshBoolean_hpp_
|
||||
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "GLGizmosCommon.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
enum class MeshBooleanSelectingState {
|
||||
Undef,
|
||||
SelectSource,
|
||||
SelectTool,
|
||||
|
||||
};
|
||||
enum class MeshBooleanOperation{
|
||||
Undef,
|
||||
Union,
|
||||
Difference,
|
||||
Intersection,
|
||||
};
|
||||
struct VolumeInfo {
|
||||
ModelVolume* mv{ nullptr };
|
||||
int volume_idx{-1};
|
||||
Transform3d trafo;
|
||||
void reset() {
|
||||
mv = nullptr;
|
||||
volume_idx = -1;
|
||||
trafo = Transform3d::Identity();
|
||||
};
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
ar(volume_idx, trafo);
|
||||
}
|
||||
};
|
||||
class GLGizmoMeshBoolean : public GLGizmoBase
|
||||
{
|
||||
public:
|
||||
GLGizmoMeshBoolean(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
~GLGizmoMeshBoolean();
|
||||
|
||||
void set_enable(bool enable) { m_enable = enable; }
|
||||
bool get_enable() { return m_enable; }
|
||||
MeshBooleanSelectingState get_selecting_state() { return m_selecting_state; }
|
||||
void set_src_volume(ModelVolume* mv) {
|
||||
m_src.mv = mv;
|
||||
if (m_src.mv == m_tool.mv)
|
||||
m_tool.reset();
|
||||
}
|
||||
void set_tool_volume(ModelVolume* mv) {
|
||||
m_tool.mv = mv;
|
||||
if (m_tool.mv == m_src.mv)
|
||||
m_src.reset();
|
||||
}
|
||||
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
|
||||
protected:
|
||||
virtual bool on_init() override;
|
||||
virtual std::string on_get_name() const override;
|
||||
virtual bool on_is_activable() const override;
|
||||
virtual void on_render() override;
|
||||
virtual void on_render_for_picking() override {}
|
||||
virtual void on_set_state() override;
|
||||
virtual CommonGizmosDataID on_get_requirements() const override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit);
|
||||
|
||||
void on_load(cereal::BinaryInputArchive &ar) override;
|
||||
void on_save(cereal::BinaryOutputArchive &ar) const override;
|
||||
|
||||
private:
|
||||
bool m_enable{ false };
|
||||
MeshBooleanOperation m_operation_mode;
|
||||
MeshBooleanSelectingState m_selecting_state;
|
||||
bool m_diff_delete_input = false;
|
||||
bool m_inter_delete_input = false;
|
||||
VolumeInfo m_src;
|
||||
VolumeInfo m_tool;
|
||||
|
||||
void generate_new_volume(bool delete_input, const TriangleMesh& mesh_result);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLGizmoMeshBoolean_hpp_
|
|
@ -212,7 +212,14 @@ void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const
|
|||
|
||||
++mesh_id;
|
||||
|
||||
const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
|
||||
Transform3d trafo_matrix;
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix();
|
||||
trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
|
||||
}
|
||||
else {
|
||||
trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix()* mv->get_matrix();
|
||||
}
|
||||
|
||||
bool is_left_handed = trafo_matrix.matrix().determinant() < 0.;
|
||||
if (is_left_handed)
|
||||
|
@ -459,14 +466,14 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
|
||||
if (m_selected_extruder_idx != extruder_idx) flags |= ImGuiColorEditFlags_NoBorder;
|
||||
#ifdef __APPLE__
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0);
|
||||
bool color_picked = ImGui::ColorButton(color_label.c_str(), color_vec, flags, button_size);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor(1);
|
||||
#else
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 2.0);
|
||||
bool color_picked = ImGui::ColorButton(color_label.c_str(), color_vec, flags, button_size);
|
||||
|
@ -512,7 +519,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
|
||||
}
|
||||
|
|
|
@ -85,10 +85,15 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
|
|||
continue;
|
||||
|
||||
++mesh_id;
|
||||
|
||||
const Transform3d trafo_matrix =
|
||||
mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
|
||||
mv->get_matrix();
|
||||
|
||||
Transform3d trafo_matrix;
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix();
|
||||
trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
|
||||
}
|
||||
else {
|
||||
trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix()* mv->get_matrix();
|
||||
}
|
||||
|
||||
bool is_left_handed = trafo_matrix.matrix().determinant() < 0.;
|
||||
if (is_left_handed)
|
||||
|
@ -124,7 +129,16 @@ void GLGizmoPainterBase::render_cursor() const
|
|||
std::vector<Transform3d> trafo_matrices;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (mv->is_model_part())
|
||||
trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix());
|
||||
{
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
Transform3d temp = mi->get_assemble_transformation().get_matrix() * mv->get_matrix();
|
||||
temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
|
||||
trafo_matrices.emplace_back(temp);
|
||||
}
|
||||
else {
|
||||
trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Raycast and return if there's no hit.
|
||||
update_raycast_cache(m_parent.get_local_mouse_position(), camera, trafo_matrices);
|
||||
|
@ -230,17 +244,35 @@ void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) co
|
|||
const BoundingBoxf3 box = bounding_box();
|
||||
Vec3d hit_world = trafo * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2));
|
||||
float max_z = (float)box.max.z();
|
||||
float min_z = (float)box.min.z();
|
||||
|
||||
float cursor_z = std::clamp((float)hit_world.z(), 0.f, max_z);
|
||||
std::array<float, 2> zs = { cursor_z, std::clamp(cursor_z + m_cursor_height, 0.f, max_z) };
|
||||
for (int i = 0; i < zs.size(); i++) {
|
||||
update_contours(zs[i], max_z);
|
||||
float cursor_z = std::clamp((float)hit_world.z(), min_z, max_z);
|
||||
std::array<float, 2> zs = { cursor_z, std::clamp(cursor_z + m_cursor_height, min_z, max_z) };
|
||||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
|
||||
const ModelInstance* mi = model_object->instances[selection.get_instance_idx()];
|
||||
for (const ModelVolume* mv : model_object->volumes) {
|
||||
TriangleMesh vol_mesh = mv->mesh();
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
Transform3d temp = mi->get_assemble_transformation().get_matrix() * mv->get_matrix();
|
||||
temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
|
||||
vol_mesh.transform(temp);
|
||||
}
|
||||
else {
|
||||
vol_mesh.transform(mi->get_transformation().get_matrix() * mv->get_matrix());
|
||||
}
|
||||
|
||||
for (int i = 0; i < zs.size(); i++) {
|
||||
update_contours(vol_mesh, zs[i], max_z, min_z);
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
|
||||
glsafe(::glLineWidth(2.0f));
|
||||
m_cut_contours.contours.render();
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
|
||||
glsafe(::glLineWidth(2.0f));
|
||||
m_cut_contours.contours.render();
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,7 +289,7 @@ BoundingBoxf3 GLGizmoPainterBase::bounding_box() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
void GLGizmoPainterBase::update_contours(float cursor_z, float max_z) const
|
||||
void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
|
@ -266,33 +298,32 @@ void GLGizmoPainterBase::update_contours(float cursor_z, float max_z) const
|
|||
const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
|
||||
const int instance_idx = selection.get_instance_idx();
|
||||
|
||||
if (0.0 < cursor_z && cursor_z < max_z) {
|
||||
if (m_cut_contours.cut_z != cursor_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) {
|
||||
m_cut_contours.cut_z = cursor_z;
|
||||
if (min_z < cursor_z && cursor_z < max_z) {
|
||||
if (m_cut_contours.cut_z != cursor_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) {
|
||||
m_cut_contours.cut_z = cursor_z;
|
||||
|
||||
if (m_cut_contours.object_id != model_object->id())
|
||||
m_cut_contours.mesh = model_object->raw_mesh();
|
||||
m_cut_contours.mesh = vol_mesh;
|
||||
|
||||
m_cut_contours.position = box.center();
|
||||
m_cut_contours.shift = Vec3d::Zero();
|
||||
m_cut_contours.object_id = model_object->id();
|
||||
m_cut_contours.instance_idx = instance_idx;
|
||||
m_cut_contours.contours.reset();
|
||||
m_cut_contours.position = box.center();
|
||||
m_cut_contours.shift = Vec3d::Zero();
|
||||
m_cut_contours.object_id = model_object->id();
|
||||
m_cut_contours.instance_idx = instance_idx;
|
||||
m_cut_contours.contours.reset();
|
||||
|
||||
MeshSlicingParams slicing_params;
|
||||
slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix();
|
||||
const Polygons polys = slice_mesh(m_cut_contours.mesh.its, cursor_z, slicing_params);
|
||||
if (!polys.empty()) {
|
||||
m_cut_contours.contours.init_from(polys, static_cast<float>(cursor_z));
|
||||
m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
|
||||
MeshSlicingParams slicing_params;
|
||||
slicing_params.trafo = Transform3d::Identity().matrix();
|
||||
const Polygons polys = slice_mesh(m_cut_contours.mesh.its, cursor_z, slicing_params);
|
||||
if (!polys.empty()) {
|
||||
m_cut_contours.contours.init_from(polys, static_cast<float>(cursor_z));
|
||||
m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
|
||||
}
|
||||
}
|
||||
else if (box.center() != m_cut_contours.position) {
|
||||
m_cut_contours.shift = box.center() - m_cut_contours.position;
|
||||
}
|
||||
}
|
||||
else if (box.center() != m_cut_contours.position) {
|
||||
m_cut_contours.shift = box.center() - m_cut_contours.position;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_cut_contours.contours.reset();
|
||||
else
|
||||
m_cut_contours.contours.reset();
|
||||
}
|
||||
|
||||
bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const
|
||||
|
@ -451,8 +482,12 @@ std::vector<GLGizmoPainterBase::ProjectedHeightRange> GLGizmoPainterBase::get_pr
|
|||
const Selection& selection = m_parent.get_selection();
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
|
||||
const Transform3d instance_trafo = mi->get_transformation().get_matrix();
|
||||
const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true);
|
||||
const Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix() :
|
||||
mi->get_transformation().get_matrix();
|
||||
const Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix(true) :
|
||||
mi->get_transformation().get_matrix(true);
|
||||
|
||||
for (int mesh_idx = 0; mesh_idx < part_volumes.size(); mesh_idx++) {
|
||||
if (mesh_idx == m_rr.mesh_id)
|
||||
|
@ -518,8 +553,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
const Selection &selection = m_parent.get_selection();
|
||||
const ModelObject *mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||
const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true);
|
||||
const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
|
||||
const Transform3d trafo_matrix_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true) :
|
||||
mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true);
|
||||
const Transform3d trafo_matrix = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix() :
|
||||
mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle,
|
||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||
|
@ -581,8 +620,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
const Selection &selection = m_parent.get_selection();
|
||||
const ModelObject *mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||
const Transform3d instance_trafo = mi->get_transformation().get_matrix();
|
||||
const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true);
|
||||
Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix() :
|
||||
mi->get_transformation().get_matrix();
|
||||
Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix(true) :
|
||||
mi->get_transformation().get_matrix(true);
|
||||
std::vector<const ModelVolume*> part_volumes;
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
|
@ -590,7 +633,14 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
std::vector<Transform3d> trafo_matrices_not_translate;
|
||||
for (const ModelVolume *mv : mo->volumes)
|
||||
if (mv->is_model_part()) {
|
||||
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
Transform3d temp = instance_trafo * mv->get_matrix();
|
||||
temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
|
||||
trafo_matrices.emplace_back(temp);
|
||||
}
|
||||
else {
|
||||
trafo_matrices.emplace_back(instance_trafo* mv->get_matrix());
|
||||
}
|
||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true));
|
||||
part_volumes.push_back(mv);
|
||||
}
|
||||
|
@ -713,15 +763,26 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
const Selection &selection = m_parent.get_selection();
|
||||
const ModelObject *mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||
const Transform3d instance_trafo = mi->get_transformation().get_matrix();
|
||||
const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true);
|
||||
const Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix() :
|
||||
mi->get_transformation().get_matrix();
|
||||
const Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
mi->get_assemble_transformation().get_matrix(true) :
|
||||
mi->get_transformation().get_matrix(true);
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
std::vector<Transform3d> trafo_matrices_not_translate;
|
||||
for (const ModelVolume *mv : mo->volumes)
|
||||
if (mv->is_model_part()) {
|
||||
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
Transform3d temp = instance_trafo * mv->get_matrix();
|
||||
temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
|
||||
trafo_matrices.emplace_back(temp);
|
||||
}
|
||||
else {
|
||||
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
|
||||
}
|
||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true));
|
||||
}
|
||||
|
||||
|
@ -811,7 +872,8 @@ void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position,
|
|||
hit,
|
||||
normal,
|
||||
m_c->object_clipper()->get_clipping_plane(),
|
||||
&facet))
|
||||
&facet,
|
||||
m_parent.get_canvas_type() != GLCanvas3D::CanvasAssembleView))
|
||||
{
|
||||
// In case this hit is clipped, skip it.
|
||||
if (is_mesh_point_clipped(hit.cast<double>(), trafo_matrices[mesh_id]))
|
||||
|
|
|
@ -368,7 +368,7 @@ private:
|
|||
mutable CutContours m_cut_contours;
|
||||
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
void update_contours(float cursor_z, float max_z) const;
|
||||
void update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const;
|
||||
|
||||
protected:
|
||||
void on_set_state() override;
|
||||
|
|
|
@ -241,7 +241,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
|||
ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
|
||||
}
|
||||
|
|
|
@ -273,8 +273,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.81f, 0.81f, 0.81f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.81f, 0.81f, 0.81f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.81f, 0.81f, 0.81f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.00f, 0.59f, 0.53f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
|
||||
if (m_imgui->bbl_sliderin("##ReductionLevel", &reduction, 0, 4, reduce_captions[reduction].c_str())) {
|
||||
if (reduction < 0) reduction = 0;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static const double PI = 3.141592653589793238;
|
||||
static const wxColour FONT_TEXTURE_BG = wxColour(0, 0, 0, 0);
|
||||
static const wxColour FONT_TEXTURE_FG = *wxWHITE;
|
||||
static const int FONT_SIZE = 12;
|
||||
|
@ -560,7 +561,7 @@ void GLGizmoText::push_button_style(bool pressed) {
|
|||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 150 / 255.f, 136 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 174 / 255.f, 66 / 255.f, 1.f));
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(45.f / 255.f, 45.f / 255.f, 49.f / 255.f, 1.f));
|
||||
|
@ -574,7 +575,7 @@ void GLGizmoText::push_button_style(bool pressed) {
|
|||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 150 / 255.f, 136 / 255.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 174 / 255.f, 66 / 255.f, 1.f));
|
||||
}
|
||||
else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.f, 1.f, 1.f, 1.f));
|
||||
|
@ -595,10 +596,10 @@ void GLGizmoText::push_combo_style(const float scale) {
|
|||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG_DARK);
|
||||
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));
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.59f, 0.53f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG_DARK);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f });
|
||||
}
|
||||
|
@ -606,10 +607,10 @@ void GLGizmoText::push_combo_style(const float scale) {
|
|||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG);
|
||||
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));
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.59f, 0.53f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f });
|
||||
}
|
||||
|
@ -1071,6 +1072,11 @@ bool GLGizmoText::update_text_positions(const std::vector<std::string>& texts)
|
|||
Vec3d rotation_axis;
|
||||
Matrix3d rotation_matrix;
|
||||
Geometry::rotation_from_two_vectors(m_cut_plane_dir, Vec3d::UnitZ(), rotation_axis, phi, &rotation_matrix);
|
||||
if (abs(phi - PI) < 1e-6) {
|
||||
Transform3d transform = Transform3d::Identity();
|
||||
transform.rotate(Eigen::AngleAxisd(phi, m_mouse_normal_world));
|
||||
rotation_matrix = transform.matrix().block<3, 3>(0, 0);
|
||||
}
|
||||
|
||||
Transform3d transfo1;
|
||||
transfo1.setIdentity();
|
||||
|
@ -1312,7 +1318,7 @@ bool GLGizmoText::update_text_positions(const std::vector<std::string>& texts)
|
|||
}
|
||||
|
||||
TriangleMesh mesh = slice_meshs;
|
||||
std::vector<double> mesh_values(m_position_points.size(), 1'000'000'000);
|
||||
std::vector<double> mesh_values(m_position_points.size(), 1e9);
|
||||
m_normal_points.resize(m_position_points.size());
|
||||
auto point_in_triangle_delete_area = [](const Vec3d &point, const Vec3d &point0, const Vec3d &point1, const Vec3d &point2) {
|
||||
Vec3d p0_p = point - point0;
|
||||
|
@ -1383,7 +1389,6 @@ TriangleMesh GLGizmoText::get_text_mesh(const char* text_str, const Vec3d &posit
|
|||
new_text_dir.normalize();
|
||||
Geometry::rotation_from_two_vectors(old_text_dir, new_text_dir, rotation_axis, phi, &rotation_matrix);
|
||||
|
||||
static double const PI = 3.141592653589793238;
|
||||
if (abs(phi - PI) < EPSILON)
|
||||
rotation_axis = normal;
|
||||
|
||||
|
@ -1466,6 +1471,9 @@ void GLGizmoText::generate_text_volume(bool is_temp)
|
|||
mesh.merge(sub_mesh);
|
||||
}
|
||||
|
||||
if (mesh.empty())
|
||||
return;
|
||||
|
||||
Plater *plater = wxGetApp().plater();
|
||||
if (!plater)
|
||||
return;
|
||||
|
|
|
@ -145,6 +145,11 @@ void InstancesHider::on_update()
|
|||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
int active_inst = get_pool()->selection_info()->get_active_instance();
|
||||
GLCanvas3D* canvas = get_pool()->get_canvas();
|
||||
double z_min;
|
||||
if (canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView)
|
||||
z_min = std::numeric_limits<double>::max();
|
||||
else
|
||||
z_min = -SINKING_Z_THRESHOLD;
|
||||
|
||||
if (mo && active_inst != -1) {
|
||||
canvas->toggle_model_objects_visibility(false);
|
||||
|
@ -152,7 +157,7 @@ void InstancesHider::on_update()
|
|||
canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst);
|
||||
canvas->set_use_clipping_planes(true);
|
||||
// Some objects may be sinking, do not show whatever is below the bed.
|
||||
canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), z_min));
|
||||
canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), std::numeric_limits<double>::max()));
|
||||
|
||||
|
||||
|
@ -164,7 +169,7 @@ void InstancesHider::on_update()
|
|||
m_clippers.clear();
|
||||
for (const TriangleMesh* mesh : meshes) {
|
||||
m_clippers.emplace_back(new MeshClipper);
|
||||
m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), z_min));
|
||||
m_clippers.back()->set_mesh(*mesh);
|
||||
}
|
||||
m_old_meshes = meshes;
|
||||
|
@ -404,18 +409,30 @@ void ObjectClipper::render_cut() const
|
|||
return;
|
||||
const SelectionInfo* sel_info = get_pool()->selection_info();
|
||||
const ModelObject* mo = sel_info->model_object();
|
||||
Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
|
||||
Geometry::Transformation inst_trafo;
|
||||
bool is_assem_cnv = get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView;
|
||||
inst_trafo = is_assem_cnv ?
|
||||
mo->instances[sel_info->get_active_instance()]->get_assemble_transformation() :
|
||||
mo->instances[sel_info->get_active_instance()]->get_transformation();
|
||||
auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly();
|
||||
|
||||
size_t clipper_id = 0;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
Geometry::Transformation vol_trafo = mv->get_transformation();
|
||||
Geometry::Transformation trafo = inst_trafo * vol_trafo;
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
|
||||
|
||||
if (is_assem_cnv) {
|
||||
trafo.set_offset(trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0) + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0));
|
||||
}
|
||||
else {
|
||||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
|
||||
}
|
||||
auto& clipper = m_clippers[clipper_id];
|
||||
clipper->set_plane(*m_clp);
|
||||
clipper->set_transformation(trafo);
|
||||
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
if (is_assem_cnv)
|
||||
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), std::numeric_limits<double>::max()));
|
||||
else
|
||||
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
glsafe(::glPushMatrix());
|
||||
// BBS
|
||||
glsafe(::glColor3f(0.25f, 0.25f, 0.25f));
|
||||
|
@ -438,14 +455,27 @@ void ObjectClipper::set_position(double pos, bool keep_normal)
|
|||
// camera_dir(2) = 0;
|
||||
|
||||
Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : /*-camera_dir;*/ -wxGetApp().plater()->get_camera().get_dir_forward();
|
||||
const Vec3d& center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift);
|
||||
Vec3d center;
|
||||
|
||||
if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
const SelectionInfo* sel_info = get_pool()->selection_info();
|
||||
auto trafo = mo->instances[sel_info->get_active_instance()]->get_assemble_transformation();
|
||||
auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly();
|
||||
center = trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0);
|
||||
}
|
||||
else {
|
||||
center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift);
|
||||
}
|
||||
|
||||
float dist = normal.dot(center);
|
||||
|
||||
if (pos < 0.)
|
||||
pos = m_clp_ratio;
|
||||
|
||||
m_clp_ratio = pos;
|
||||
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2*m_active_inst_bb_radius)));
|
||||
|
||||
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2 * m_active_inst_bb_radius)));
|
||||
|
||||
get_pool()->get_canvas()->set_as_dirty();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoText.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
|
||||
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
@ -54,9 +55,16 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent)
|
|||
std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
|
||||
{
|
||||
std::vector<size_t> out;
|
||||
for (size_t i=0; i<m_gizmos.size(); ++i)
|
||||
if (m_gizmos[i]->is_selectable())
|
||||
out.push_back(i);
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
for (size_t i = 0; i < m_gizmos.size(); ++i)
|
||||
if (m_gizmos[i]->get_sprite_id() == (unsigned int)MmuSegmentation)
|
||||
out.push_back(i);
|
||||
}
|
||||
else {
|
||||
for (size_t i = 0; i < m_gizmos.size(); ++i)
|
||||
if (m_gizmos[i]->is_selectable())
|
||||
out.push_back(i);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -78,6 +86,8 @@ size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const
|
|||
#if BBS_TOOLBAR_ON_TOP
|
||||
//float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();;
|
||||
float top_x = std::max(m_parent.get_main_toolbar_width() + border, 0.5f * (cnv_w - width + m_parent.get_main_toolbar_width() + m_parent.get_collapse_toolbar_width() - m_parent.get_assemble_view_toolbar_width()) + border);
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView)
|
||||
top_x = 0.5f * cnv_w + 0.5f * (m_parent.get_assembly_paint_toolbar_width());
|
||||
float top_y = 0;
|
||||
float stride_x = m_layout.scaled_stride_x();
|
||||
|
||||
|
@ -161,7 +171,11 @@ void GLGizmosManager::switch_gizmos_icon_filename()
|
|||
case(EType::MmuSegmentation):
|
||||
gizmo->set_icon_filename(m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg");
|
||||
break;
|
||||
case(EType::MeshBoolean):
|
||||
gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,6 +205,7 @@ bool GLGizmosManager::init()
|
|||
m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg", EType::Scale, &m_object_manipulation));
|
||||
m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg", EType::Flatten));
|
||||
m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut));
|
||||
m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg", EType::MeshBoolean));
|
||||
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg", EType::FdmSupports));
|
||||
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam));
|
||||
m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text));
|
||||
|
@ -629,6 +644,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
|||
return dynamic_cast<GLGizmoText*>(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Cut)
|
||||
return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == MeshBoolean)
|
||||
return dynamic_cast<GLGizmoMeshBoolean*>(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
@ -875,7 +892,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) {
|
||||
if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ||
|
||||
m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut)
|
||||
m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut || m_current == MeshBoolean)
|
||||
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown()))
|
||||
// the gizmo got the event and took some action, there is no need to do anything more
|
||||
processed = true;
|
||||
|
@ -965,6 +982,17 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||
update_on_off_state(mouse_pos);
|
||||
update_data();
|
||||
m_parent.set_as_dirty();
|
||||
try {
|
||||
if ((int)m_hover >= 0 && (int)m_hover < m_gizmos.size()) {
|
||||
std::string name = get_name_from_gizmo_etype(m_hover);
|
||||
int count = m_gizmos[m_hover]->get_count();
|
||||
NetworkAgent* agent = GUI::wxGetApp().getAgent();
|
||||
if (agent) {
|
||||
agent->track_update_property(name, std::to_string(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
else if (evt.MiddleDown()) {
|
||||
|
@ -1372,17 +1400,23 @@ void GLGizmosManager::do_render_overlay() const
|
|||
float width = get_scaled_total_width();
|
||||
float zoomed_border = m_layout.scaled_border() * inv_zoom;
|
||||
|
||||
//BBS: GUI refactor: GLToolbar&&Gizmo adjust
|
||||
float zoomed_top_x;
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
zoomed_top_x = 0.5f * m_parent.get_assembly_paint_toolbar_width() * inv_zoom;
|
||||
}
|
||||
else {
|
||||
//BBS: GUI refactor: GLToolbar&&Gizmo adjust
|
||||
#if BBS_TOOLBAR_ON_TOP
|
||||
float main_toolbar_width = (float)m_parent.get_main_toolbar_width();
|
||||
float assemble_view_width = (float)m_parent.get_assemble_view_toolbar_width();
|
||||
float collapse_width = (float)m_parent.get_collapse_toolbar_width();
|
||||
//float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();
|
||||
//float zoomed_top_x = 0.5f *(cnv_w + main_toolbar_width - 2 * space_width - width) * inv_zoom;
|
||||
float main_toolbar_width = (float)m_parent.get_main_toolbar_width();
|
||||
float assemble_view_width = (float)m_parent.get_assemble_view_toolbar_width();
|
||||
float collapse_width = (float)m_parent.get_collapse_toolbar_width();
|
||||
//float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();
|
||||
//float zoomed_top_x = 0.5f *(cnv_w + main_toolbar_width - 2 * space_width - width) * inv_zoom;
|
||||
|
||||
float main_toolbar_left = std::max(-0.5f * cnv_w, -0.5f * (main_toolbar_width + get_scaled_total_width() + assemble_view_width - collapse_width)) * inv_zoom;
|
||||
//float zoomed_top_x = 0.5f *(main_toolbar_width + collapse_width - width - assemble_view_width) * inv_zoom;
|
||||
float zoomed_top_x = main_toolbar_left + (main_toolbar_width) *inv_zoom;
|
||||
float main_toolbar_left = std::max(-0.5f * cnv_w, -0.5f * (main_toolbar_width + get_scaled_total_width() + assemble_view_width - collapse_width)) * inv_zoom;
|
||||
//float zoomed_top_x = 0.5f *(main_toolbar_width + collapse_width - width - assemble_view_width) * inv_zoom;
|
||||
zoomed_top_x = main_toolbar_left + (main_toolbar_width)*inv_zoom;
|
||||
}
|
||||
float zoomed_top_y = 0.5f * cnv_h * inv_zoom;
|
||||
#else
|
||||
//float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom;
|
||||
|
@ -1653,5 +1687,28 @@ int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const
|
|||
return m_gizmos[type]->get_shortcut_key();
|
||||
}
|
||||
|
||||
std::string get_name_from_gizmo_etype(GLGizmosManager::EType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GLGizmosManager::EType::Move:
|
||||
return "Move";
|
||||
case GLGizmosManager::EType::Rotate:
|
||||
return "Rotate";
|
||||
case GLGizmosManager::EType::Scale:
|
||||
return "Scale";
|
||||
case GLGizmosManager::EType::Flatten:
|
||||
return "Flatten";
|
||||
case GLGizmosManager::EType::FdmSupports:
|
||||
return "FdmSupports";
|
||||
case GLGizmosManager::EType::Seam:
|
||||
return "Seam";
|
||||
case GLGizmosManager::EType::Text:
|
||||
return "Text";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -70,6 +70,7 @@ public:
|
|||
Scale,
|
||||
Flatten,
|
||||
Cut,
|
||||
MeshBoolean,
|
||||
FdmSupports,
|
||||
Seam,
|
||||
// BBS
|
||||
|
@ -327,7 +328,7 @@ private:
|
|||
bool grabber_contains_mouse() const;
|
||||
};
|
||||
|
||||
|
||||
std::string get_name_from_gizmo_etype(GLGizmosManager::EType type);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -452,6 +452,7 @@ void GizmoObjectManipulation::set_uniform_scaling(const bool new_value)
|
|||
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
|
||||
if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
|
||||
// Cannot apply scaling in the world coordinate system.
|
||||
// BBS: remove tilt prompt dialog
|
||||
|
|
|
@ -241,7 +241,7 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3
|
|||
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane,
|
||||
size_t* facet_idx) const
|
||||
size_t* facet_idx, bool sinking_limit) const
|
||||
{
|
||||
Vec3d point;
|
||||
Vec3d direction;
|
||||
|
@ -258,8 +258,8 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
|
|||
// Also, remove anything below the bed (sinking objects).
|
||||
for (i=0; i<hits.size(); ++i) {
|
||||
Vec3d transformed_hit = trafo * hits[i].position();
|
||||
if (transformed_hit.z() >= SINKING_Z_THRESHOLD &&
|
||||
(! clipping_plane || ! clipping_plane->is_point_clipped(transformed_hit)))
|
||||
if (transformed_hit.z() >= (sinking_limit ? SINKING_Z_THRESHOLD : -std::numeric_limits<double>::max()) &&
|
||||
(!clipping_plane || !clipping_plane->is_point_clipped(transformed_hit)))
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,8 @@ public:
|
|||
Vec3f& position, // where to save the positibon of the hit (mesh coords)
|
||||
Vec3f& normal, // normal of the triangle that was hit
|
||||
const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
|
||||
size_t* facet_idx = nullptr // index of the facet hit
|
||||
size_t* facet_idx = nullptr, // index of the facet hit
|
||||
bool sinking_limit = true
|
||||
) const;
|
||||
|
||||
// Given a vector of points in woorld coordinates, this returns vector
|
||||
|
|
|
@ -306,6 +306,96 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
|
|||
if (fading_pop)
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::bbl_render_block_notification(GLCanvas3D &canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin)
|
||||
{
|
||||
if (m_state == EState::Unknown)
|
||||
init();
|
||||
|
||||
if (m_state == EState::Hidden) {
|
||||
m_top_y = initial_y - GAP_WIDTH;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state == EState::ClosePending || m_state == EState::Finished)
|
||||
{
|
||||
m_state = EState::Finished;
|
||||
return;
|
||||
}
|
||||
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
float right_gap = right_margin + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
|
||||
bool fading_pop = false;
|
||||
|
||||
if (m_line_height != ImGui::CalcTextSize("A").y)
|
||||
init();
|
||||
|
||||
set_next_window_size(imgui);
|
||||
|
||||
// top y of window
|
||||
m_top_y = initial_y + m_window_height;
|
||||
|
||||
ImVec2 win_pos(1.0f * (float) cnv_size.get_width() - right_gap, 1.0f * (float) cnv_size.get_height() - m_top_y);
|
||||
imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
|
||||
imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
|
||||
|
||||
// color change based on fading out
|
||||
if (m_state == EState::FadingOut) {
|
||||
push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered), true, m_current_fade_opacity);
|
||||
fading_pop = true;
|
||||
}
|
||||
|
||||
bool bgrnd_color_pop = push_background_color();
|
||||
|
||||
// name of window indentifies window - has to be unique string
|
||||
if (m_id == 0)
|
||||
m_id = m_id_provider.allocate_id();
|
||||
std::string name = "!!Ntfctn" + std::to_string(m_id);
|
||||
|
||||
use_bbl_theme();
|
||||
if (m_data.level == NotificationLevel::SeriousWarningNotificationLevel)
|
||||
{
|
||||
push_style_color(ImGuiCol_Border, {245.f / 255.f, 155 / 255.f, 22 / 255.f, 1}, true, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_WindowBg, {245.f / 255.f, 155 / 255.f, 22 / 255.f, 1}, true, m_current_fade_opacity);
|
||||
}
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel) {
|
||||
push_style_color(ImGuiCol_Border, {225.f / 255.f, 71 / 255.f, 71 / 255.f, 1}, true, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_WindowBg, {225.f / 255.f, 71 / 255.f, 71 / 255.f, 1}, true, m_current_fade_opacity);
|
||||
}
|
||||
push_style_color(ImGuiCol_Text, { 1,1,1,1 }, true, m_current_fade_opacity);
|
||||
|
||||
if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
ImVec2 win_size = ImGui::GetWindowSize();
|
||||
ImVec2 win_pos = ImGui::GetWindowPos();
|
||||
if (ImGui::IsMouseHoveringRect(win_pos, win_pos + win_size)) {
|
||||
set_hovered();
|
||||
}
|
||||
|
||||
//render left icon
|
||||
bbl_render_block_notif_left_sign(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
|
||||
bbl_render_block_notif_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
|
||||
bbl_render_block_notif_buttons(imgui, win_size, win_pos);
|
||||
//render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
|
||||
//m_minimize_b_visible = false;
|
||||
//if (m_multiline && m_lines_count > 3)
|
||||
// render_minimize_button(imgui, win_pos.x, win_pos.y);
|
||||
}
|
||||
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
|
||||
imgui.end();
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
restore_default_theme();
|
||||
|
||||
if (bgrnd_color_pop)
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (fading_pop)
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
bool NotificationManager::PopNotification::push_background_color()
|
||||
{
|
||||
if (m_is_gray) {
|
||||
|
@ -345,6 +435,10 @@ void NotificationManager::PopNotification::count_spaces()
|
|||
// m_left_indentation = picture_width + m_line_height / 2;
|
||||
//}
|
||||
m_window_width_offset = m_left_indentation + m_line_height * 3.f;
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::SeriousWarningNotificationLevel) {
|
||||
m_left_indentation = 32 + m_line_height;
|
||||
m_window_width_offset = 90.f;
|
||||
}
|
||||
m_window_width = m_line_height * 25;
|
||||
}
|
||||
|
||||
|
@ -464,6 +558,68 @@ void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& im
|
|||
m_window_height += 1 * m_line_height; // top and bottom
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::bbl_render_block_notif_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
float x_offset = m_left_indentation;
|
||||
int last_end = 0;
|
||||
float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height : (m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2 : m_line_height / 2));
|
||||
float shift_y = m_line_height;
|
||||
std::string line;
|
||||
|
||||
for (size_t i = 0; i < (m_multiline ? m_endlines.size() : std::min(m_endlines.size(), (size_t)2)); i++) {
|
||||
assert(m_endlines.size() > i && m_text1.size() >= m_endlines[i]);
|
||||
line.clear();
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
|
||||
if (i == 1 && m_endlines.size() > 2 && !m_multiline) {
|
||||
// second line with "more" hypertext
|
||||
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
|
||||
line = line.substr(0, line.length() - 1);
|
||||
}
|
||||
line += "..";
|
||||
}
|
||||
else {
|
||||
// regural line
|
||||
line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
||||
}
|
||||
last_end = m_endlines[i];
|
||||
if (m_text1.size() > m_endlines[i])
|
||||
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
||||
|
||||
if (pos_start != string::npos && pos_end != string::npos && m_endlines[i] - line.length() >= pos_start && m_endlines[i] <= pos_end) {
|
||||
push_style_color(ImGuiCol_Text, m_ErrorColor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
imgui.text(line.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
else {
|
||||
imgui.text(line.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
//hyperlink text
|
||||
if (!m_multiline && m_lines_count > 2) {
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true);
|
||||
}
|
||||
else if (!m_hypertext.empty()) {
|
||||
const ImVec2 button_size = {52, 28};
|
||||
ImVec2 cursor_pos = { win_size_x - m_line_height * 6.0f, win_size_y / 2 - button_size.y / 2 };
|
||||
ImGui::SetCursorPos(cursor_pos);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 2);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.f, 1.f, 1.f, 1.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.f, 1.f, 1.f, 0.f));
|
||||
//if (imgui.button(_L("Jump"), button_size.x, button_size.y)) { if (on_text_click()) { close(); } }
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleVar(2);
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext);
|
||||
}
|
||||
|
||||
// text2 (text after hypertext) is not rendered for regular notifications
|
||||
// its rendering is in HintNotification::render_text
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
float x_offset = m_left_indentation;
|
||||
|
@ -539,9 +695,18 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
|||
ImGui::PopStyleColor(3);
|
||||
|
||||
//hover color
|
||||
ImVec4 HyperColor = m_HyperTextColor;
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
|
||||
HyperColor.y += 0.1f;
|
||||
ImVec4 HyperColor = m_HyperTextColor;//ImVec4(150.f / 255.f, 100.f / 255.f, 0.f / 255.f, 1)
|
||||
if (m_data.level == NotificationLevel::SeriousWarningNotificationLevel)
|
||||
HyperColor = ImVec4(0.f, 0.f, 0.f, 0.4f);
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel)
|
||||
HyperColor = ImVec4(135.f / 255.f, 43 / 255.f, 43 / 255.f, 1);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
|
||||
{
|
||||
HyperColor.y += 0.1f;
|
||||
if (m_data.level == NotificationLevel::SeriousWarningNotificationLevel || m_data.level == NotificationLevel::SeriousWarningNotificationLevel)
|
||||
HyperColor.x += 0.2f;
|
||||
}
|
||||
|
||||
|
||||
//text
|
||||
push_style_color(ImGuiCol_Text, HyperColor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
|
@ -607,6 +772,64 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
|||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::bbl_render_block_notif_buttons(ImGuiWrapper& imgui, ImVec2 win_size, ImVec2 win_pos)
|
||||
{
|
||||
// close button
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
std::wstring button_text;
|
||||
button_text = ImGui::CloseBlockNotifButton;
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(into_u8(button_text).c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 2.f, button_pic_size.y * 2.f);
|
||||
|
||||
ImVec2 cursor_pos = { win_size.x - m_line_height * 2.0f, win_size.y / 2 - button_size.y / 2 };
|
||||
ImGui::SetCursorPos(cursor_pos);
|
||||
//if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y + win_size.y / 2 - button_pic_size.y),
|
||||
// ImVec2(win_pos.x - win_size.x / 20.f, win_pos.y + win_size.y / 2 + button_pic_size.y),
|
||||
// true))
|
||||
if(ImGui::IsMouseHoveringRect(win_pos + cursor_pos, win_pos + cursor_pos + button_size))
|
||||
{
|
||||
button_text = ImGui::CloseBlockNotifHoverButton;
|
||||
}
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
close();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
|
||||
|
||||
//// minimize button
|
||||
//ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
//ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
//push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
//push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
//push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
////button - if part if treggered
|
||||
//std::wstring button_text;
|
||||
//button_text = m_is_dark ? ImGui::MinimalizeDarkButton : ImGui::MinimalizeButton;
|
||||
//if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1),
|
||||
// ImVec2(win_pos_x, win_pos_y + m_window_height),
|
||||
// true))
|
||||
//{
|
||||
// button_text = m_is_dark ? ImGui::MinimalizeHoverDarkButton : ImGui::MinimalizeHoverButton;
|
||||
//}
|
||||
//ImVec2 button_pic_size = ImGui::CalcTextSize(into_u8(button_text).c_str());
|
||||
//ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
//ImGui::SetCursorPosX(m_window_width - m_line_height * 1.8f);
|
||||
//ImGui::SetCursorPosY(m_window_height - button_size.y - 5);
|
||||
//if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
//{
|
||||
// m_multiline = false;
|
||||
//}
|
||||
|
||||
//ImGui::PopStyleColor(5);
|
||||
//m_minimize_b_visible = true;
|
||||
}
|
||||
|
||||
//void NotificationManager::PopNotification::render_multiline(ImGuiWrapper &imgui, const float win_pos_x, const float win_pos_y)
|
||||
//{
|
||||
// if (m_data.type == NotificationType::BBLObjectInfo)
|
||||
|
@ -615,6 +838,19 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
|||
// }
|
||||
//}
|
||||
|
||||
void NotificationManager::PopNotification::bbl_render_block_notif_left_sign(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
//window->DrawList->AddImage(user_texture_id, bb.Min + padding + margin, bb.Max - padding - margin, uv0, uv1, ImGui::GetColorU32(tint_col));
|
||||
|
||||
std::wstring text;
|
||||
text = ImGui::BlockNotifErrorIcon;
|
||||
ImGui::SetCursorPosX(m_line_height / 3);
|
||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||
imgui.text(text.c_str());
|
||||
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::bbl_render_left_sign(ImGuiWrapper &imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
|
@ -1630,19 +1866,39 @@ void NotificationManager::push_validate_error_notification(StringObjectException
|
|||
set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
void NotificationManager::push_slicing_error_notification(const std::string &text, ModelObject const *obj)
|
||||
void NotificationManager::push_slicing_error_notification(const std::string &text, std::vector<ModelObject const *> objs)
|
||||
{
|
||||
auto callback = obj ? [id = obj->id()](wxEvtHandler *) {
|
||||
std::vector<ObjectID> ids;
|
||||
for (auto optr : objs) {
|
||||
if (optr)
|
||||
ids.push_back(optr->id());
|
||||
}
|
||||
auto callback = !objs.empty() ? [ids](wxEvtHandler *) {
|
||||
auto & objects = wxGetApp().model().objects;
|
||||
auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; });
|
||||
if (iter != objects.end()) {
|
||||
wxGetApp().obj_list()->select_items({{*iter, nullptr}});
|
||||
std::vector<ObjectVolumeID> ovs;
|
||||
for (auto id : ids) {
|
||||
auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; });
|
||||
if (iter != objects.end()) { ovs.push_back({*iter, nullptr}); }
|
||||
}
|
||||
if (!ovs.empty()) {
|
||||
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
|
||||
wxGetApp().obj_list()->select_items(ovs);
|
||||
}
|
||||
return false;
|
||||
} : std::function<bool(wxEvtHandler *)>();
|
||||
auto link = callback ? _u8L("Jump to") : "";
|
||||
if (obj) link += std::string(" [") + obj->name + "]";
|
||||
if (!objs.empty()) {
|
||||
link += " [";
|
||||
for (auto obj : objs) {
|
||||
if (obj)
|
||||
link += obj->name + ", ";
|
||||
}
|
||||
if (!objs.empty()) {
|
||||
link.pop_back();
|
||||
link.pop_back();
|
||||
}
|
||||
link += "] ";
|
||||
}
|
||||
set_all_slicing_errors_gray(false);
|
||||
push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("Error:") + "\n" + text, link, callback }, 0);
|
||||
set_slicing_progress_hidden();
|
||||
|
@ -1653,8 +1909,8 @@ void NotificationManager::push_slicing_warning_notification(const std::string& t
|
|||
auto & objects = wxGetApp().model().objects;
|
||||
auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; });
|
||||
if (iter != objects.end()) {
|
||||
wxGetApp().obj_list()->select_items({{*iter, nullptr}});
|
||||
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
|
||||
wxGetApp().obj_list()->select_items({{*iter, nullptr}});
|
||||
}
|
||||
return false;
|
||||
} : std::function<bool(wxEvtHandler *)>();
|
||||
|
@ -1869,6 +2125,53 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
|
|||
}
|
||||
}
|
||||
|
||||
void NotificationManager::push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelObject const *> objs)
|
||||
{
|
||||
std::vector<ObjectID> ids;
|
||||
for (auto optr : objs) {
|
||||
if (optr) ids.push_back(optr->id());
|
||||
}
|
||||
auto callback = !objs.empty() ?
|
||||
[ids](wxEvtHandler *) {
|
||||
auto & objects = wxGetApp().model().objects;
|
||||
std::vector<ObjectVolumeID> ovs;
|
||||
for (auto id : ids) {
|
||||
auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; });
|
||||
if (iter != objects.end()) { ovs.push_back({*iter, nullptr}); }
|
||||
}
|
||||
if (!ovs.empty()) {
|
||||
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
|
||||
wxGetApp().obj_list()->select_items(ovs);
|
||||
}
|
||||
return false;
|
||||
} :
|
||||
std::function<bool(wxEvtHandler *)>();
|
||||
auto link = callback ? _u8L("Jump to") : "";
|
||||
if (!objs.empty()) {
|
||||
link += " [";
|
||||
for (auto obj : objs) {
|
||||
if (obj) link += obj->name + ", ";
|
||||
}
|
||||
if (!objs.empty()) {
|
||||
link.pop_back();
|
||||
link.pop_back();
|
||||
}
|
||||
link += "] ";
|
||||
}
|
||||
set_all_slicing_warnings_gray(false);
|
||||
push_notification_data({NotificationType::SlicingSeriousWarning, NotificationLevel::SeriousWarningNotificationLevel, 0, _u8L("Serious warning:") + "\n" + text, link,
|
||||
callback},
|
||||
0);
|
||||
set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
void NotificationManager::close_slicing_serious_warning_notification(const std::string &text)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingSeriousWarning && notification->compare_text(_u8L("Serious warning:") + "\n" + text)) { notification->close(); }
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::init_slicing_progress_notification(std::function<bool()> cancel_callback)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
|
@ -2114,12 +2417,13 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
|
|||
if (m_initialized) { // ignore update action - it cant be initialized if canvas and imgui context is not ready
|
||||
if (notification->get_type() == NotificationType::SlicingWarning) {
|
||||
m_pop_notifications.back()->append(notification->get_data().ori_text);
|
||||
}
|
||||
else
|
||||
m_pop_notifications.back()->update(notification->get_data());
|
||||
} else {
|
||||
m_pop_notifications.back()->update(notification->get_data());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_pop_notifications.emplace_back(std::move(notification));
|
||||
|
||||
retval = true;
|
||||
}
|
||||
if (!m_initialized)
|
||||
|
@ -2129,7 +2433,10 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
|
|||
return retval;
|
||||
}
|
||||
|
||||
void NotificationManager::push_delayed_notification_data(std::unique_ptr<NotificationManager::PopNotification> notification, std::function<bool(void)> condition_callback, int64_t initial_delay, int64_t delay_interval)
|
||||
void NotificationManager::push_delayed_notification_data(std::unique_ptr<NotificationManager::PopNotification> notification,
|
||||
std::function<bool(void)> condition_callback,
|
||||
int64_t initial_delay,
|
||||
int64_t delay_interval)
|
||||
{
|
||||
if (initial_delay == 0 && condition_callback()) {
|
||||
if( push_notification_data(std::move(notification), 0))
|
||||
|
@ -2155,13 +2462,20 @@ void NotificationManager::render_notifications(GLCanvas3D &canvas, float overlay
|
|||
{
|
||||
sort_notifications();
|
||||
|
||||
float last_y = bottom_margin * m_scale;
|
||||
float bottom_up_last_y = bottom_margin * m_scale;
|
||||
|
||||
for (const auto& notification : m_pop_notifications) {
|
||||
if (notification->get_state() != PopNotification::EState::Hidden) {
|
||||
notification->render(canvas, last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale);
|
||||
if (notification->get_state() != PopNotification::EState::Finished)
|
||||
last_y = notification->get_top() + GAP_WIDTH;
|
||||
if (notification->get_data().level == NotificationLevel::ErrorNotificationLevel || notification->get_data().level == NotificationLevel::SeriousWarningNotificationLevel) {
|
||||
notification->bbl_render_block_notification(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale);
|
||||
if (notification->get_state() != PopNotification::EState::Finished)
|
||||
bottom_up_last_y = notification->get_top() + GAP_WIDTH;
|
||||
}
|
||||
else {
|
||||
if (notification->get_state() != PopNotification::EState::Hidden) {
|
||||
notification->render(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale);
|
||||
if (notification->get_state() != PopNotification::EState::Finished)
|
||||
bottom_up_last_y = notification->get_top() + GAP_WIDTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_last_render = GLCanvas3D::timestamp_now();
|
||||
|
@ -2295,6 +2609,8 @@ void NotificationManager::set_in_preview(bool preview)
|
|||
notification->hide(preview);
|
||||
if (m_in_preview && notification->get_type() == NotificationType::DidYouKnowHint)
|
||||
notification->close();
|
||||
if (notification->get_type() == NotificationType::ValidateWarning)
|
||||
notification->hide(preview);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ enum class NotificationType
|
|||
// Slicing error produced by BackgroundSlicingProcess::validate() or by the BackgroundSlicingProcess background
|
||||
// thread thowing a SlicingError exception.
|
||||
SlicingError,
|
||||
//Gcode conflict generates slicing severe warning
|
||||
SlicingSeriousWarning,
|
||||
// Slicing warnings, issued by the slicing process.
|
||||
// Slicing warnings are registered for a particular Print milestone or a PrintObject and its milestone.
|
||||
SlicingWarning,
|
||||
|
@ -162,6 +164,8 @@ public:
|
|||
ImportantNotificationLevel,
|
||||
// Warning, no fade-out.
|
||||
WarningNotificationLevel,
|
||||
// Serious warning, bold reminder
|
||||
SeriousWarningNotificationLevel,
|
||||
// Error, no fade-out. Top most position.
|
||||
ErrorNotificationLevel,
|
||||
};
|
||||
|
@ -191,8 +195,10 @@ public:
|
|||
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
|
||||
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
void push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelObject const *> objs);
|
||||
void close_slicing_serious_warning_notification(const std::string &text);
|
||||
// Creates Slicing Error notification with a custom text and no fade out.
|
||||
void push_slicing_error_notification(const std::string &text, ModelObject const *obj);
|
||||
void push_slicing_error_notification(const std::string &text, std::vector<ModelObject const *> objs);
|
||||
// Creates Slicing Warning notification with a custom text and no fade out.
|
||||
void push_slicing_warning_notification(const std::string &text, bool gray, ModelObject const *obj, ObjectID oid, int warning_step, int warning_msg_id);
|
||||
// marks slicing errors as gray
|
||||
|
@ -375,6 +381,7 @@ private:
|
|||
PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler);
|
||||
virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); }
|
||||
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin);
|
||||
virtual void bbl_render_block_notification(GLCanvas3D &canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin);
|
||||
// close will dissapear notification on next render
|
||||
virtual void close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);}
|
||||
// data from newer notification of same type
|
||||
|
@ -384,6 +391,8 @@ private:
|
|||
void reinit() { m_state = EState::Unknown; }
|
||||
// returns top after movement
|
||||
float get_top() const { return m_top_y; }
|
||||
// returns bottom after movement
|
||||
float get_bottom() const { return m_bottom_y; }
|
||||
//returns top in actual frame
|
||||
float get_current_top() const { return m_top_y; }
|
||||
const NotificationType get_type() const { return m_data.type; }
|
||||
|
@ -420,6 +429,13 @@ private:
|
|||
const float text_x, const float text_y,
|
||||
const std::string text,
|
||||
bool more = false);
|
||||
virtual void bbl_render_block_notif_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
virtual void bbl_render_block_notif_buttons(ImGuiWrapper& imgui,
|
||||
ImVec2 win_size,
|
||||
ImVec2 win_pos);
|
||||
virtual void bbl_render_block_notif_left_sign(ImGuiWrapper &imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y);
|
||||
// Left sign could be error or warning sign
|
||||
virtual void bbl_render_left_sign(ImGuiWrapper &imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y);
|
||||
|
||||
|
@ -511,6 +527,8 @@ private:
|
|||
float m_window_width { 450.0f };
|
||||
//Distance from bottom of notifications to top of this notification
|
||||
float m_top_y { 0.0f };
|
||||
//Distance from top of block notifications to bottom of this notification
|
||||
float m_bottom_y { 0.0f };
|
||||
// Height of text - Used as basic scaling unit!
|
||||
float m_line_height;
|
||||
// endlines for text1, hypertext excluded
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#include "format.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include <wx/dcgraph.h>
|
||||
using boost::optional;
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
@ -577,7 +577,7 @@ void PartPlate::render_logo_texture(GLTexture &logo_texture, const GeometryBuffe
|
|||
}
|
||||
}
|
||||
|
||||
void PartPlate::render_logo(bool bottom) const
|
||||
void PartPlate::render_logo(bool bottom, bool render_cali) const
|
||||
{
|
||||
if (!m_partplate_list->render_bedtype_logo) {
|
||||
// render third-party printer texture logo
|
||||
|
@ -656,6 +656,7 @@ void PartPlate::render_logo(bool bottom) const
|
|||
}
|
||||
|
||||
m_partplate_list->load_bedtype_textures();
|
||||
m_partplate_list->load_cali_textures();
|
||||
|
||||
// btDefault should be skipped
|
||||
auto curr_bed_type = get_bed_type();
|
||||
|
@ -665,6 +666,7 @@ void PartPlate::render_logo(bool bottom) const
|
|||
curr_bed_type = proj_cfg.opt_enum<BedType>(std::string("curr_bed_type"));
|
||||
}
|
||||
int bed_type_idx = (int)curr_bed_type;
|
||||
// render bed textures
|
||||
for (auto &part : m_partplate_list->bed_texture_info[bed_type_idx].parts) {
|
||||
if (part.texture) {
|
||||
if (part.buffer && part.buffer->get_vertices_count() > 0
|
||||
|
@ -681,6 +683,24 @@ void PartPlate::render_logo(bool bottom) const
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render cali texture
|
||||
if (render_cali) {
|
||||
for (auto& part : m_partplate_list->cali_texture_info.parts) {
|
||||
if (part.texture) {
|
||||
if (part.buffer && part.buffer->get_vertices_count() > 0) {
|
||||
if (part.offset.x() != m_origin.x() || part.offset.y() != m_origin.y()) {
|
||||
part.offset = Vec2d(m_origin.x(), m_origin.y());
|
||||
part.update_buffer();
|
||||
}
|
||||
render_logo_texture(*(part.texture),
|
||||
*(part.buffer),
|
||||
bottom,
|
||||
part.vbo_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PartPlate::render_exclude_area(bool force_default_color) const {
|
||||
|
@ -1311,6 +1331,14 @@ std::vector<int> PartPlate::get_extruders(bool conside_custom_gcode) const
|
|||
plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end());
|
||||
}
|
||||
|
||||
// layer range
|
||||
for (auto layer_range : mo->layer_config_ranges) {
|
||||
if (layer_range.second.has("extruder")) {
|
||||
if (auto id = layer_range.second.option("extruder")->getInt(); id > 0)
|
||||
plate_extruders.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool obj_support = false;
|
||||
const ConfigOption* obj_support_opt = mo->config.option("enable_support");
|
||||
const ConfigOption *obj_raft_opt = mo->config.option("raft_layers");
|
||||
|
@ -1365,6 +1393,142 @@ std::vector<int> PartPlate::get_extruders(bool conside_custom_gcode) const
|
|||
return plate_extruders;
|
||||
}
|
||||
|
||||
std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig& full_config) const
|
||||
{
|
||||
std::vector<int> plate_extruders;
|
||||
|
||||
// if 3mf file
|
||||
int glb_support_intf_extr = full_config.opt_int("support_interface_filament");
|
||||
int glb_support_extr = full_config.opt_int("support_filament");
|
||||
bool glb_support = full_config.opt_bool("enable_support");
|
||||
glb_support |= full_config.opt_int("raft_layers") > 0;
|
||||
|
||||
for (std::set<std::pair<int, int>>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it)
|
||||
{
|
||||
int obj_id = it->first;
|
||||
int instance_id = it->second;
|
||||
|
||||
if ((obj_id >= 0) && (obj_id < m_model->objects.size()))
|
||||
{
|
||||
ModelObject* object = m_model->objects[obj_id];
|
||||
ModelInstance* instance = object->instances[instance_id];
|
||||
|
||||
if (!instance->printable)
|
||||
continue;
|
||||
|
||||
for (ModelVolume* mv : object->volumes) {
|
||||
std::vector<int> volume_extruders = mv->get_extruders();
|
||||
plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end());
|
||||
}
|
||||
|
||||
// layer range
|
||||
for (auto layer_range : object->layer_config_ranges) {
|
||||
if (layer_range.second.has("extruder")) {
|
||||
if (auto id = layer_range.second.option("extruder")->getInt(); id > 0)
|
||||
plate_extruders.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool obj_support = false;
|
||||
const ConfigOption* obj_support_opt = object->config.option("enable_support");
|
||||
const ConfigOption *obj_raft_opt = object->config.option("raft_layers");
|
||||
if (obj_support_opt != nullptr || obj_raft_opt != nullptr) {
|
||||
if (obj_support_opt != nullptr)
|
||||
obj_support = obj_support_opt->getBool();
|
||||
if (obj_raft_opt != nullptr)
|
||||
obj_support |= obj_raft_opt->getInt() > 0;
|
||||
}
|
||||
else
|
||||
obj_support = glb_support;
|
||||
|
||||
if (!obj_support)
|
||||
continue;
|
||||
|
||||
int obj_support_intf_extr = 0;
|
||||
const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament");
|
||||
if (support_intf_extr_opt != nullptr)
|
||||
obj_support_intf_extr = support_intf_extr_opt->getInt();
|
||||
if (obj_support_intf_extr != 0)
|
||||
plate_extruders.push_back(obj_support_intf_extr);
|
||||
else if (glb_support_intf_extr != 0)
|
||||
plate_extruders.push_back(glb_support_intf_extr);
|
||||
|
||||
int obj_support_extr = 0;
|
||||
const ConfigOption* support_extr_opt = object->config.option("support_filament");
|
||||
if (support_extr_opt != nullptr)
|
||||
obj_support_extr = support_extr_opt->getInt();
|
||||
if (obj_support_extr != 0)
|
||||
plate_extruders.push_back(obj_support_extr);
|
||||
else if (glb_support_extr != 0)
|
||||
plate_extruders.push_back(glb_support_extr);
|
||||
}
|
||||
}
|
||||
|
||||
if (conside_custom_gcode) {
|
||||
//BBS
|
||||
int nums_extruders = 0;
|
||||
if (const ConfigOptionStrings *color_option = dynamic_cast<const ConfigOptionStrings *>(full_config.option("filament_colour"))) {
|
||||
nums_extruders = color_option->values.size();
|
||||
if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) {
|
||||
for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders)
|
||||
plate_extruders.push_back(item.extruder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(plate_extruders.begin(), plate_extruders.end());
|
||||
auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end());
|
||||
plate_extruders.resize(std::distance(plate_extruders.begin(), it_end));
|
||||
return plate_extruders;
|
||||
}
|
||||
|
||||
std::vector<int> PartPlate::get_extruders_without_support(bool conside_custom_gcode) const
|
||||
{
|
||||
std::vector<int> plate_extruders;
|
||||
// if gcode.3mf file
|
||||
if (m_model->objects.empty()) {
|
||||
for (int i = 0; i < slice_filaments_info.size(); i++) {
|
||||
plate_extruders.push_back(slice_filaments_info[i].id + 1);
|
||||
}
|
||||
return plate_extruders;
|
||||
}
|
||||
|
||||
// if 3mf file
|
||||
const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
|
||||
for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) {
|
||||
if (!contain_instance_totally(obj_idx, 0))
|
||||
continue;
|
||||
|
||||
ModelObject* mo = m_model->objects[obj_idx];
|
||||
for (ModelVolume* mv : mo->volumes) {
|
||||
std::vector<int> volume_extruders = mv->get_extruders();
|
||||
plate_extruders.insert(plate_extruders.end(), volume_extruders.begin(), volume_extruders.end());
|
||||
}
|
||||
}
|
||||
|
||||
if (conside_custom_gcode) {
|
||||
//BBS
|
||||
int nums_extruders = 0;
|
||||
if (const ConfigOptionStrings* color_option = dynamic_cast<const ConfigOptionStrings*>(wxGetApp().preset_bundle->project_config.option("filament_colour"))) {
|
||||
nums_extruders = color_option->values.size();
|
||||
if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) {
|
||||
for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders)
|
||||
plate_extruders.push_back(item.extruder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(plate_extruders.begin(), plate_extruders.end());
|
||||
auto it_end = std::unique(plate_extruders.begin(), plate_extruders.end());
|
||||
plate_extruders.resize(std::distance(plate_extruders.begin(), it_end));
|
||||
return plate_extruders;
|
||||
}
|
||||
|
||||
std::vector<int> PartPlate::get_used_extruders()
|
||||
{
|
||||
std::vector<int> used_extruders;
|
||||
|
@ -1916,6 +2080,47 @@ void PartPlate::translate_all_instance(Vec3d position)
|
|||
return;
|
||||
}
|
||||
|
||||
void PartPlate::duplicate_all_instance(unsigned int dup_count, bool need_skip, std::map<int, bool>& skip_objects)
|
||||
{
|
||||
std::set<std::pair<int, int>> old_obj_list = obj_to_instance_set;
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": plate_id %1%, dup_count %2%") % m_plate_index % dup_count;
|
||||
for (std::set<std::pair<int, int>>::iterator it = old_obj_list.begin(); it != old_obj_list.end(); ++it)
|
||||
{
|
||||
int obj_id = it->first;
|
||||
int instance_id = it->second;
|
||||
|
||||
if ((obj_id >= 0) && (obj_id < m_model->objects.size()))
|
||||
{
|
||||
ModelObject* object = m_model->objects[obj_id];
|
||||
ModelInstance* instance = object->instances[instance_id];
|
||||
|
||||
if (need_skip)
|
||||
{
|
||||
if (skip_objects.find(instance->loaded_id) != skip_objects.end())
|
||||
{
|
||||
instance->printable = false;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": skipped object, loaded_id %1%, name %2%, set to unprintable, no need to duplicate") % instance->loaded_id % object->name;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < dup_count; index ++)
|
||||
{
|
||||
ModelObject* newObj = m_model->add_object(*object);
|
||||
newObj->name = object->name +"_"+ std::to_string(index+1);
|
||||
int new_obj_id = m_model->objects.size() - 1;
|
||||
for ( size_t new_instance_id = 0; new_instance_id < object->instances.size(); new_instance_id++ )
|
||||
{
|
||||
obj_to_instance_set.emplace(std::pair(new_obj_id, new_instance_id));
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": duplicate object into plate: index_pair [%1%,%2%], obj_id %3%") % new_obj_id % new_instance_id % newObj->id().id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//update instance exclude state
|
||||
void PartPlate::update_instance_exclude_status(int obj_id, int instance_id, BoundingBoxf3* bounding_box)
|
||||
|
@ -2012,6 +2217,28 @@ bool PartPlate::has_printable_instances()
|
|||
return result;
|
||||
}
|
||||
|
||||
bool PartPlate::is_all_instances_unprintable()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
for (std::set<std::pair<int, int>>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) {
|
||||
int obj_id = it->first;
|
||||
int instance_id = it->second;
|
||||
|
||||
if (obj_id >= m_model->objects.size()) continue;
|
||||
|
||||
ModelObject * object = m_model->objects[obj_id];
|
||||
ModelInstance *instance = object->instances[instance_id];
|
||||
|
||||
if ((instance->printable)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//move instances to left or right PartPlate
|
||||
void PartPlate::move_instances_to(PartPlate& left_plate, PartPlate& right_plate, BoundingBoxf3* bounding_box)
|
||||
{
|
||||
|
@ -2301,7 +2528,7 @@ bool PartPlate::intersects(const BoundingBoxf3& bb) const
|
|||
return print_volume.intersects(bb);
|
||||
}
|
||||
|
||||
void PartPlate::render(bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id)
|
||||
void PartPlate::render(bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id, bool render_cali)
|
||||
{
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
|
@ -2318,7 +2545,10 @@ void PartPlate::render(bool bottom, bool only_body, bool force_background_color,
|
|||
render_grid(bottom);
|
||||
|
||||
if (!bottom && m_selected && !force_background_color) {
|
||||
render_logo(bottom);
|
||||
if (m_partplate_list)
|
||||
render_logo(bottom, m_partplate_list->render_cali_logo && render_cali);
|
||||
else
|
||||
render_logo(bottom);
|
||||
}
|
||||
|
||||
render_height_limit(mode);
|
||||
|
@ -2838,6 +3068,7 @@ void PartPlateList::release_icon_textures()
|
|||
}
|
||||
//reset
|
||||
PartPlateList::is_load_bedtype_textures = false;
|
||||
PartPlateList::is_load_cali_texture = false;
|
||||
for (int i = 0; i < btCount; i++) {
|
||||
for (auto& part: bed_texture_info[i].parts) {
|
||||
if (part.texture) {
|
||||
|
@ -3944,8 +4175,7 @@ bool PartPlateList::preprocess_nonprefered_areas(arrangement::ArrangePolygons& r
|
|||
bool added = false;
|
||||
|
||||
std::vector<BoundingBoxf> nonprefered_regions;
|
||||
nonprefered_regions.emplace_back(Vec2d{ 45,0 }, Vec2d{ 225,25 }); // extrusion calibration region
|
||||
nonprefered_regions.emplace_back(Vec2d{ 25,0 }, Vec2d{ 50,60 }); // hand-eye calibration region
|
||||
nonprefered_regions.emplace_back(Vec2d{ 18,0 }, Vec2d{ 240,15 }); // new extrusion & hand-eye calibration region
|
||||
|
||||
//has exclude areas
|
||||
PartPlate* plate = m_plate_list[0];
|
||||
|
@ -4102,7 +4332,7 @@ void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arr
|
|||
|
||||
/*rendering related functions*/
|
||||
//render
|
||||
void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id)
|
||||
void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali)
|
||||
{
|
||||
const std::lock_guard<std::mutex> local_lock(m_plates_mutex);
|
||||
std::vector<PartPlate*>::iterator it = m_plate_list.begin();
|
||||
|
@ -4127,15 +4357,15 @@ void PartPlateList::render(bool bottom, bool only_current, bool only_body, int h
|
|||
if (current_index == m_current_plate) {
|
||||
PartPlate::HeightLimitMode height_mode = (only_current)?PartPlate::HEIGHT_LIMIT_NONE:m_height_limit_mode;
|
||||
if (plate_hover_index == current_index)
|
||||
(*it)->render(bottom, only_body, false, height_mode, plate_hover_action);
|
||||
(*it)->render(bottom, only_body, false, height_mode, plate_hover_action, render_cali);
|
||||
else
|
||||
(*it)->render(bottom, only_body, false, height_mode, -1);
|
||||
(*it)->render(bottom, only_body, false, height_mode, -1, render_cali);
|
||||
}
|
||||
else {
|
||||
if (plate_hover_index == current_index)
|
||||
(*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action);
|
||||
(*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action, render_cali);
|
||||
else
|
||||
(*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1);
|
||||
(*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4323,14 +4553,23 @@ bool PartPlateList::is_all_slice_results_valid() const
|
|||
//check whether all plates's slice result valid for print
|
||||
bool PartPlateList::is_all_slice_results_ready_for_print() const
|
||||
{
|
||||
for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i)
|
||||
{
|
||||
if (!m_plate_list[i]->is_slice_result_ready_for_print()
|
||||
&& m_plate_list[i]->has_printable_instances()
|
||||
)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
bool res = false;
|
||||
|
||||
for (unsigned int i = 0; i < (unsigned int) m_plate_list.size(); ++i) {
|
||||
if (!m_plate_list[i]->empty()) {
|
||||
if (m_plate_list[i]->is_all_instances_unprintable()) {
|
||||
continue;
|
||||
}
|
||||
if (!m_plate_list[i]->is_slice_result_ready_for_print()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (m_plate_list[i]->is_slice_result_ready_for_print()) {
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -4564,6 +4803,7 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w
|
|||
plate_data_item->gcode_prediction = std::to_string(
|
||||
(int) m_plate_list[i]->get_slice_result()->print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
|
||||
plate_data_item->toolpath_outside = m_plate_list[i]->m_gcode_result->toolpath_outside;
|
||||
plate_data_item->is_label_object_enabled = m_plate_list[i]->m_gcode_result->label_object_enabled;
|
||||
Print *print = nullptr;
|
||||
m_plate_list[i]->get_print((PrintBase **) &print, nullptr, nullptr);
|
||||
if (print) {
|
||||
|
@ -4612,8 +4852,8 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list)
|
|||
{
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate index %1% seems invalid, skip it")% plate_data_list[i]->plate_index;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5%")
|
||||
%i %plate_data_list[i]->gcode_file %plate_data_list[i]->is_sliced_valid %plate_data_list[i]->toolpath_outside %plate_data_list[i]->is_support_used;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5% is_label_object_enabled %6%")
|
||||
%i %plate_data_list[i]->gcode_file %plate_data_list[i]->is_sliced_valid %plate_data_list[i]->toolpath_outside %plate_data_list[i]->is_support_used %plate_data_list[i]->is_label_object_enabled;
|
||||
//load object and instance from 3mf
|
||||
//just test for file correct or not, we will rebuild later
|
||||
/*for (std::vector<std::pair<int, int>>::iterator it = plate_data_list[i]->objects_and_instances.begin(); it != plate_data_list[i]->objects_and_instances.end(); ++it)
|
||||
|
@ -4634,6 +4874,7 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list)
|
|||
}
|
||||
ps.total_used_filament *= 1000; //koef
|
||||
gcode_result->toolpath_outside = plate_data_list[i]->toolpath_outside;
|
||||
gcode_result->label_object_enabled = plate_data_list[i]->is_label_object_enabled;
|
||||
m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info;
|
||||
gcode_result->warnings = plate_data_list[i]->warnings;
|
||||
if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) {
|
||||
|
@ -4722,6 +4963,7 @@ void PartPlateList::print() const
|
|||
}
|
||||
|
||||
bool PartPlateList::is_load_bedtype_textures = false;
|
||||
bool PartPlateList::is_load_cali_texture = false;
|
||||
|
||||
void PartPlateList::BedTextureInfo::TexturePart::update_buffer()
|
||||
{
|
||||
|
@ -4812,5 +5054,39 @@ void PartPlateList::load_bedtype_textures()
|
|||
PartPlateList::is_load_bedtype_textures = true;
|
||||
}
|
||||
|
||||
void PartPlateList::init_cali_texture_info()
|
||||
{
|
||||
BedTextureInfo::TexturePart cali_line(18, 2, 224, 16, "bbl_cali_lines.svg");
|
||||
cali_texture_info.parts.push_back(cali_line);
|
||||
|
||||
for (int j = 0; j < cali_texture_info.parts.size(); j++) {
|
||||
cali_texture_info.parts[j].update_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
void PartPlateList::load_cali_textures()
|
||||
{
|
||||
if (PartPlateList::is_load_cali_texture) return;
|
||||
|
||||
init_cali_texture_info();
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048;
|
||||
for (int i = 0; i < (unsigned int)btCount; ++i) {
|
||||
for (int j = 0; j < cali_texture_info.parts.size(); j++) {
|
||||
std::string filename = resources_dir() + "/images/" + cali_texture_info.parts[j].filename;
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
PartPlateList::cali_texture_info.parts[j].texture = new GLTexture();
|
||||
if (!PartPlateList::cali_texture_info.parts[j].texture->load_from_svg_file(filename, true, true, true, logo_tex_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
PartPlateList::is_load_cali_texture = true;
|
||||
}
|
||||
|
||||
}//end namespace GUI
|
||||
}//end namespace slic3r
|
||||
|
|
|
@ -170,10 +170,8 @@ private:
|
|||
void calc_vertex_for_number(int index, bool one_number, GeometryBuffer &buffer);
|
||||
void calc_vertex_for_icons(int index, GeometryBuffer &buffer);
|
||||
void calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer);
|
||||
//void calc_vertex_for_name_tex(GeometryBuffer &buffer);
|
||||
|
||||
void render_background(bool force_default_color = false) const;
|
||||
void render_logo(bool bottom) const;
|
||||
void render_logo(bool bottom, bool render_cali = true) const;
|
||||
void render_logo_texture(GLTexture& logo_texture, const GeometryBuffer& logo_buffer, bool bottom, unsigned int vbo_id) const;
|
||||
void render_exclude_area(bool force_default_color) const;
|
||||
//void render_background_for_picking(const float* render_color) const;
|
||||
|
@ -288,6 +286,8 @@ public:
|
|||
Vec3d get_origin() { return m_origin; }
|
||||
Vec3d estimate_wipe_tower_size(const double w, const double wipe_volume) const;
|
||||
std::vector<int> get_extruders(bool conside_custom_gcode = false) const;
|
||||
std::vector<int> get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig& full_config) const;
|
||||
std::vector<int> get_extruders_without_support(bool conside_custom_gcode = false) const;
|
||||
std::vector<int> get_used_extruders();
|
||||
|
||||
/* instance related operations*/
|
||||
|
@ -315,6 +315,9 @@ public:
|
|||
//translate instance on the plate
|
||||
void translate_all_instance(Vec3d position);
|
||||
|
||||
//duplicate all instance for count
|
||||
void duplicate_all_instance(unsigned int dup_count, bool need_skip, std::map<int, bool>& skip_objects);
|
||||
|
||||
//update instance exclude state
|
||||
void update_instance_exclude_status(int obj_id, int instance_id, BoundingBoxf3* bounding_box = nullptr);
|
||||
|
||||
|
@ -328,6 +331,7 @@ public:
|
|||
|
||||
//whether it is has printable instances
|
||||
bool has_printable_instances();
|
||||
bool is_all_instances_unprintable();
|
||||
|
||||
//move instances to left or right PartPlate
|
||||
void move_instances_to(PartPlate& left_plate, PartPlate& right_plate, BoundingBoxf3* bounding_box = nullptr);
|
||||
|
@ -340,7 +344,7 @@ public:
|
|||
bool contains(const BoundingBoxf3& bb) const;
|
||||
bool intersects(const BoundingBoxf3& bb) const;
|
||||
|
||||
void render(bool bottom, bool only_body = false, bool force_background_color = false, HeightLimitMode mode = HEIGHT_LIMIT_NONE, int hover_id = -1);
|
||||
void render(bool bottom, bool only_body = false, bool force_background_color = false, HeightLimitMode mode = HEIGHT_LIMIT_NONE, int hover_id = -1, bool render_cali = false);
|
||||
void render_for_picking() const { on_render_for_picking(); }
|
||||
void set_selected();
|
||||
void set_unselected();
|
||||
|
@ -400,7 +404,7 @@ public:
|
|||
{
|
||||
bool result = m_slice_result_valid;
|
||||
if (result)
|
||||
result = m_gcode_result ? (!m_gcode_result->toolpath_outside && !m_gcode_result->conflict_result.has_value()) : false;
|
||||
result = m_gcode_result ? (!m_gcode_result->toolpath_outside) : false;// && !m_gcode_result->conflict_result.has_value() gcode conflict can also print
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -524,6 +528,7 @@ class PartPlateList : public ObjectBase
|
|||
// set render option
|
||||
bool render_bedtype_logo = true;
|
||||
bool render_plate_settings = true;
|
||||
bool render_cali_logo = true;
|
||||
|
||||
bool m_is_dark = false;
|
||||
|
||||
|
@ -587,6 +592,7 @@ public:
|
|||
static const unsigned int MAX_PLATES_COUNT = MAX_PLATE_COUNT;
|
||||
static GLTexture bed_textures[(unsigned int)btCount];
|
||||
static bool is_load_bedtype_textures;
|
||||
static bool is_load_cali_texture;
|
||||
|
||||
PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF);
|
||||
PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF);
|
||||
|
@ -732,9 +738,10 @@ public:
|
|||
|
||||
/*rendering related functions*/
|
||||
void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; }
|
||||
void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1);
|
||||
void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false);
|
||||
void render_for_picking_pass();
|
||||
void set_render_option(bool bedtype_texture, bool plate_settings);
|
||||
void set_render_cali(bool value = true) { render_cali_logo = value; }
|
||||
BoundingBoxf3& get_bounding_box() { return m_bounding_box; }
|
||||
//int select_plate_by_hover_id(int hover_id);
|
||||
int select_plate_by_obj(int obj_index, int instance_index);
|
||||
|
@ -792,7 +799,12 @@ public:
|
|||
void init_bed_type_info();
|
||||
void load_bedtype_textures();
|
||||
|
||||
void show_cali_texture(bool show = true);
|
||||
void init_cali_texture_info();
|
||||
void load_cali_textures();
|
||||
|
||||
BedTextureInfo bed_texture_info[btCount];
|
||||
BedTextureInfo cali_texture_info;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -60,6 +60,10 @@
|
|||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
// For stl export
|
||||
#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp"
|
||||
#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
|
@ -169,6 +173,8 @@ wxDEFINE_EVENT(EVT_INSTALL_PLUGIN_HINT, wxCommandEvent);
|
|||
wxDEFINE_EVENT(EVT_PREVIEW_ONLY_MODE_HINT, wxCommandEvent);
|
||||
//BBS: change light/dark mode
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_COLOR_MODE_CHANGED, SimpleEvent);
|
||||
//BBS: print
|
||||
wxDEFINE_EVENT(EVT_PRINT_FROM_SDCARD_VIEW, SimpleEvent);
|
||||
|
||||
|
||||
bool Plater::has_illegal_filename_characters(const wxString& wxs_name)
|
||||
|
@ -528,8 +534,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->m_text_printer_settings = new Label(p->m_panel_printer_title, _L("Printer"), LB_PROPAGATE_MOUSE_EVENT);
|
||||
|
||||
p->m_printer_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
auto wizard_t = new ConfigWizard(wxGetApp().mainframe);
|
||||
wizard_t->run(ConfigWizard::RR_USER, ConfigWizard::SP_CUSTOM);
|
||||
//auto wizard_t = new ConfigWizard(wxGetApp().mainframe);
|
||||
//wizard_t->run(ConfigWizard::RR_USER, ConfigWizard::SP_CUSTOM);
|
||||
});
|
||||
|
||||
|
||||
|
@ -619,11 +625,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||
m_bed_type_list = new ComboBox(p->m_panel_printer_content, wxID_ANY, wxString(""), wxDefaultPosition, {-1, FromDIP(30)}, 0, nullptr, wxCB_READONLY);
|
||||
const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
|
||||
if (bed_type_def && bed_type_def->enum_keys_map) {
|
||||
for (auto item : *bed_type_def->enum_keys_map) {
|
||||
if (item.first == "Default Plate")
|
||||
continue;
|
||||
|
||||
m_bed_type_list->AppendString(_L(item.first));
|
||||
for (auto item : bed_type_def->enum_labels) {
|
||||
m_bed_type_list->AppendString(_L(item));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1628,6 +1631,10 @@ bool Sidebar::show_object_list(bool show) const
|
|||
{
|
||||
if (!p->m_object_list->Show(show))
|
||||
return false;
|
||||
if (!show)
|
||||
p->object_layers->Show(false);
|
||||
else
|
||||
p->m_object_list->part_selection_changed();
|
||||
p->scrolled->Layout();
|
||||
return true;
|
||||
}
|
||||
|
@ -1731,6 +1738,7 @@ struct Plater::priv
|
|||
bool m_slice_all{false};
|
||||
bool m_is_slicing {false};
|
||||
bool m_is_publishing {false};
|
||||
int m_is_RightClickInLeftUI{-1};
|
||||
int m_cur_slice_plate;
|
||||
//BBS: m_slice_all in .gcode.3mf file case, set true when slice all
|
||||
bool m_slice_all_only_has_gcode{ false };
|
||||
|
@ -1906,6 +1914,12 @@ struct Plater::priv
|
|||
bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); }
|
||||
void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
|
||||
|
||||
bool is_view3D_overhang_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_overhang_shown(); }
|
||||
void show_view3D_overhang(bool show)
|
||||
{
|
||||
if (current_panel == view3D) view3D->get_canvas3d()->show_overhang(show);
|
||||
}
|
||||
|
||||
bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); }
|
||||
void collapse_sidebar(bool collapse);
|
||||
|
||||
|
@ -2115,6 +2129,7 @@ struct Plater::priv
|
|||
//void show_action_buttons(const bool is_ready_to_slice) const;
|
||||
bool show_publish_dlg(bool show = true);
|
||||
void update_publish_dialog_status(wxString &msg, int percent = -1);
|
||||
void on_action_print_plate_from_sdcard(SimpleEvent&);
|
||||
|
||||
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
|
||||
// triangulate the bed and store the triangles into m_bed.m_triangles,
|
||||
|
@ -2227,6 +2242,9 @@ private:
|
|||
std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
|
||||
bool show_warning_dialog { false };
|
||||
|
||||
//record print preset
|
||||
void record_start_print_preset(std::string action);
|
||||
|
||||
};
|
||||
|
||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf)", std::regex::icase);
|
||||
|
@ -2499,6 +2517,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
assemble_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
|
||||
assemble_canvas->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); });
|
||||
assemble_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
|
||||
assemble_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
||||
assemble_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
||||
}
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
|
@ -2512,6 +2532,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
q->Bind(EVT_GLTOOLBAR_SLICE_PLATE, &priv::on_action_slice_plate, this);
|
||||
q->Bind(EVT_GLTOOLBAR_SLICE_ALL, &priv::on_action_slice_all, this);
|
||||
q->Bind(EVT_GLTOOLBAR_PRINT_PLATE, &priv::on_action_print_plate, this);
|
||||
q->Bind(EVT_PRINT_FROM_SDCARD_VIEW, &priv::on_action_print_plate_from_sdcard, this);
|
||||
q->Bind(EVT_GLTOOLBAR_SELECT_SLICED_PLATE, &priv::on_action_select_sliced_plate, this);
|
||||
q->Bind(EVT_GLTOOLBAR_PRINT_ALL, &priv::on_action_print_all, this);
|
||||
q->Bind(EVT_GLTOOLBAR_EXPORT_GCODE, &priv::on_action_export_gcode, this);
|
||||
|
@ -3701,12 +3722,13 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
// update printable state for new volumes on canvas3D
|
||||
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
|
||||
|
||||
Selection& selection = view3D->get_canvas3d()->get_selection();
|
||||
selection.clear();
|
||||
for (size_t idx : obj_idxs) {
|
||||
selection.add_object((unsigned int)idx, false);
|
||||
if (!load_config) {
|
||||
Selection& selection = view3D->get_canvas3d()->get_selection();
|
||||
selection.clear();
|
||||
for (size_t idx : obj_idxs) {
|
||||
selection.add_object((unsigned int)idx, false);
|
||||
}
|
||||
}
|
||||
|
||||
// BBS: update object list selection
|
||||
this->sidebar->obj_list()->update_selections();
|
||||
|
||||
|
@ -3776,17 +3798,15 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
const double max_ratio = std::max(ratio(0), ratio(1));
|
||||
if (max_ratio > 10000) {
|
||||
MessageDialog dlg(q, _L("Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?"), _L("Object too large"),
|
||||
wxICON_QUESTION | wxYES_NO);
|
||||
wxICON_QUESTION | wxYES);
|
||||
int answer = dlg.ShowModal();
|
||||
if (answer == wxID_YES) {
|
||||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||
// so scale down the mesh
|
||||
object->scale_mesh_after_creation(1. / max_ratio);
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
break;
|
||||
}
|
||||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||
// so scale down the mesh
|
||||
object->scale_mesh_after_creation(1. / max_ratio);
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
break;
|
||||
}
|
||||
else if (max_ratio > 10) {
|
||||
MessageDialog dlg(q, _L("Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?"), _L("Object too large"),
|
||||
|
@ -3836,8 +3856,14 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
for (auto& instance : new_instances) {
|
||||
auto offset = instance->get_offset();
|
||||
auto start_point = this->bed.build_volume().bounding_volume2d().center();
|
||||
auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({ start_point(0),start_point(1) });
|
||||
Vec3d displacement = { empty_cell.x(),empty_cell.y(),offset(2)};
|
||||
bool plate_empty = partplate_list.get_curr_plate()->empty();
|
||||
Vec3d displacement;
|
||||
if (plate_empty)
|
||||
displacement = {start_point(0), start_point(1), offset(2)};
|
||||
else {
|
||||
auto empty_cell = wxGetApp().plater()->canvas3D()->get_nearest_empty_cell({start_point(0), start_point(1)});
|
||||
displacement = {empty_cell.x(), empty_cell.y(), offset(2)};
|
||||
}
|
||||
instance->set_offset(displacement);
|
||||
}
|
||||
#endif
|
||||
|
@ -3852,9 +3878,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
//}
|
||||
|
||||
notification_manager->close_notification_of_type(NotificationType::UpdatedItemsInfo);
|
||||
for (const size_t idx : obj_idxs) {
|
||||
wxGetApp().obj_list()->add_object_to_list(idx);
|
||||
}
|
||||
wxGetApp().obj_list()->add_objects_to_list(obj_idxs);
|
||||
|
||||
update();
|
||||
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
|
||||
|
@ -4166,6 +4190,7 @@ void Plater::priv::reset(bool apply_presets_change)
|
|||
|
||||
if (view3D->is_layers_editing_enabled())
|
||||
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
|
||||
view3D->get_canvas3d()->reset_all_gizmos();
|
||||
|
||||
reset_gcode_toolpaths();
|
||||
//BBS: update gcode to current partplate's
|
||||
|
@ -4185,6 +4210,7 @@ void Plater::priv::reset(bool apply_presets_change)
|
|||
// Stop and reset the Print content.
|
||||
this->background_process.reset();
|
||||
model.clear_objects();
|
||||
assemble_view->get_canvas3d()->reset_explosion_ratio();
|
||||
update();
|
||||
|
||||
//BBS
|
||||
|
@ -4505,13 +4531,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
|
||||
(return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) {
|
||||
// The background processing was killed and it will not be restarted.
|
||||
// Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages.
|
||||
SlicingProcessCompletedEvent evt(EVT_PROCESS_COMPLETED, 0,
|
||||
SlicingProcessCompletedEvent::Cancelled, nullptr);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%, post an EVT_PROCESS_COMPLETED to main, status %2%")%__LINE__ %evt.status();
|
||||
wxQueueEvent(q, evt.Clone());
|
||||
}
|
||||
// The background processing was killed and it will not be restarted.
|
||||
// Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages.
|
||||
SlicingProcessCompletedEvent evt(EVT_PROCESS_COMPLETED, 0,
|
||||
SlicingProcessCompletedEvent::Cancelled, nullptr);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%, post an EVT_PROCESS_COMPLETED to main, status %2%")%__LINE__ %evt.status();
|
||||
wxQueueEvent(q, evt.Clone());
|
||||
}
|
||||
|
||||
if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||
{
|
||||
|
@ -4636,7 +4662,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
|
|||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||
return;
|
||||
|
||||
show_warning_dialog = false;
|
||||
show_warning_dialog = true;
|
||||
if (! output_path.empty()) {
|
||||
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
|
||||
notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0);
|
||||
|
@ -4668,7 +4694,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
|
|||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||
return;
|
||||
|
||||
show_warning_dialog = false;
|
||||
show_warning_dialog = true;
|
||||
if (! output_path.empty()) {
|
||||
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
|
||||
notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0);
|
||||
|
@ -5103,7 +5129,7 @@ void Plater::priv::reload_from_disk()
|
|||
if (has_source && old_volume->source.object_idx < int(new_model.objects.size())) {
|
||||
const ModelObject *obj = new_model.objects[old_volume->source.object_idx];
|
||||
if (old_volume->source.volume_idx < int(obj->volumes.size())) {
|
||||
if (obj->volumes[old_volume->source.volume_idx]->name == old_volume->name) {
|
||||
if (obj->volumes[old_volume->source.volume_idx]->source.input_file == old_volume->source.input_file) {
|
||||
new_volume_idx = old_volume->source.volume_idx;
|
||||
new_object_idx = old_volume->source.object_idx;
|
||||
match_found = true;
|
||||
|
@ -5574,15 +5600,16 @@ void Plater::priv::on_select_bed_type(wxCommandEvent &evt)
|
|||
{
|
||||
ComboBox* combo = static_cast<ComboBox*>(evt.GetEventObject());
|
||||
int selection = combo->GetSelection();
|
||||
wxString bed_type_name = combo->GetString(selection);
|
||||
std::string bed_type_name = print_config_def.get("curr_bed_type")->enum_values[selection];
|
||||
|
||||
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
|
||||
DynamicPrintConfig& proj_config = wxGetApp().preset_bundle->project_config;
|
||||
const t_config_enum_values* keys_map = print_config_def.get("curr_bed_type")->enum_keys_map;
|
||||
|
||||
if (keys_map) {
|
||||
BedType new_bed_type = btCount;
|
||||
for (auto item : *keys_map) {
|
||||
if (_L(item.first) == bed_type_name) {
|
||||
if (item.first == bed_type_name) {
|
||||
new_bed_type = (BedType)item.second;
|
||||
break;
|
||||
}
|
||||
|
@ -5924,21 +5951,24 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||
// This bool stops showing export finished notification even when process_completed_with_error is false
|
||||
bool has_error = false;
|
||||
if (evt.error()) {
|
||||
std::pair<std::string, size_t> message = evt.format_error_message();
|
||||
auto message = evt.format_error_message();
|
||||
if (evt.critical_error()) {
|
||||
if (q->m_tracking_popup_menu) {
|
||||
// We don't want to pop-up a message box when tracking a pop-up menu.
|
||||
// We postpone the error message instead.
|
||||
q->m_tracking_popup_menu_error_message = message.first;
|
||||
} else {
|
||||
show_error(q, message.first, message.second != 0);
|
||||
show_error(q, message.first, message.second.size() != 0 && message.second[0] != 0);
|
||||
notification_manager->set_slicing_progress_hidden();
|
||||
}
|
||||
} else {
|
||||
ModelObject const *model_object = nullptr;
|
||||
const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(message.second));
|
||||
if (print_object) model_object = print_object->model_object();
|
||||
notification_manager->push_slicing_error_notification(message.first, model_object);
|
||||
std::vector<const ModelObject *> ptrs;
|
||||
for (auto oid : message.second)
|
||||
{
|
||||
const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(oid));
|
||||
if (print_object) { ptrs.push_back(print_object->model_object()); }
|
||||
}
|
||||
notification_manager->push_slicing_error_notification(message.first, ptrs);
|
||||
}
|
||||
if (evt.invalidate_plater())
|
||||
{
|
||||
|
@ -6219,6 +6249,18 @@ void Plater::priv::on_action_print_plate(SimpleEvent&)
|
|||
m_select_machine_dlg->ShowModal();
|
||||
}
|
||||
|
||||
void Plater::priv::on_action_print_plate_from_sdcard(SimpleEvent&)
|
||||
{
|
||||
if (q != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print plate event\n";
|
||||
}
|
||||
|
||||
//BBS
|
||||
if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q);
|
||||
m_select_machine_dlg->prepare(0);
|
||||
m_select_machine_dlg->ShowModal();
|
||||
}
|
||||
|
||||
|
||||
void Plater::priv::on_action_send_to_printer(bool isall)
|
||||
{
|
||||
|
@ -7278,18 +7320,19 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
|
|||
tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle");
|
||||
}
|
||||
}
|
||||
const GLGizmosManager& gizmos = view3D->get_canvas3d()->get_gizmos_manager();
|
||||
const GLGizmosManager& gizmos = get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager();
|
||||
|
||||
if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator)
|
||||
this->undo_redo_stack().clear();
|
||||
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), partplate_list, snapshot_data);
|
||||
this->undo_redo_stack().take_snapshot(snapshot_name, model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : view3D->get_canvas3d()->get_selection(), gizmos, partplate_list, snapshot_data);
|
||||
if (snapshot_type == UndoRedo::SnapshotType::LeavingGizmoWithAction) {
|
||||
// Filter all but the last UndoRedo::SnapshotType::GizmoAction in a row between the last UndoRedo::SnapshotType::EnteringGizmo and UndoRedo::SnapshotType::LeavingGizmoWithAction.
|
||||
// The remaining snapshot will be renamed to a more generic name,
|
||||
// depending on what gizmo is being left.
|
||||
assert(gizmos.get_current() != nullptr);
|
||||
std::string new_name = gizmos.get_current()->get_action_snapshot_name();
|
||||
this->undo_redo_stack().reduce_noisy_snapshots(new_name);
|
||||
if (gizmos.get_current() != nullptr) {
|
||||
std::string new_name = gizmos.get_current()->get_action_snapshot_name();
|
||||
this->undo_redo_stack().reduce_noisy_snapshots(new_name);
|
||||
}
|
||||
} else if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator) {
|
||||
// Reset the "dirty project" flag.
|
||||
m_undo_redo_stack_main.mark_current_as_saved();
|
||||
|
@ -7311,6 +7354,13 @@ void Plater::priv::undo()
|
|||
// BBS: undo-redo until modify record
|
||||
while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current));
|
||||
if (it_current == snapshots.begin()) return;
|
||||
if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction &&
|
||||
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::EnteringGizmo &&
|
||||
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoNoAction &&
|
||||
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoWithAction)
|
||||
return;
|
||||
}
|
||||
this->undo_redo_to(it_current);
|
||||
}
|
||||
|
||||
|
@ -7413,8 +7463,8 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||
const UndoRedo::Snapshot snapshot_copy = *it_snapshot;
|
||||
// Do the jump in time.
|
||||
if (it_snapshot->timestamp < this->undo_redo_stack().active_snapshot_time() ?
|
||||
this->undo_redo_stack().undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) :
|
||||
this->undo_redo_stack().redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) {
|
||||
this->undo_redo_stack().undo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) :
|
||||
this->undo_redo_stack().redo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) {
|
||||
if (printer_technology_changed) {
|
||||
// Switch to the other printer technology. Switch to the last printer active for that particular technology.
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
|
@ -7491,7 +7541,7 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||
|
||||
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
|
||||
{
|
||||
this->view3D->get_canvas3d()->get_selection().clear();
|
||||
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear();
|
||||
// Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies).
|
||||
this->update((unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE | (unsigned int)UpdateParams::POSTPONE_VALIDATION_ERROR_MESSAGE);
|
||||
// Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot.
|
||||
|
@ -7500,8 +7550,12 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo
|
|||
// triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size.
|
||||
this->undo_redo_stack().release_least_recently_used();
|
||||
//YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time)
|
||||
this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances);
|
||||
this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot);
|
||||
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
assemble_view->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances) :
|
||||
this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances);
|
||||
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
|
||||
assemble_view->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot) :
|
||||
this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot);
|
||||
|
||||
wxGetApp().obj_list()->update_after_undo_redo();
|
||||
|
||||
|
@ -7653,6 +7707,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
|
|||
//get_partplate_list().update_slice_context_to_current_plate(p->background_process);
|
||||
//p->preview->update_gcode_result(p->partplate_list.get_current_slice_result());
|
||||
reset(transfer_preset_changes);
|
||||
reset_project_dirty_after_save();
|
||||
reset_project_dirty_initial_presets();
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
update_project_dirty_from_presets();
|
||||
|
@ -7777,6 +7832,9 @@ void Plater::load_project(wxString const& filename2,
|
|||
p->camera.requires_zoom_to_plate = REQUIRES_ZOOM_TO_ALL_PLATE;
|
||||
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
|
||||
}
|
||||
else {
|
||||
p->partplate_list.select_plate_view();
|
||||
}
|
||||
|
||||
if (previous_gcode)
|
||||
collapse_sidebar(false);
|
||||
|
@ -7814,7 +7872,7 @@ int Plater::save_project(bool saveAs)
|
|||
return wxID_CANCEL;
|
||||
|
||||
//BBS export 3mf without gcode
|
||||
if (export_3mf(into_path(filename), SaveStrategy::SplitModel) < 0) {
|
||||
if (export_3mf(into_path(filename), SaveStrategy::SplitModel | SaveStrategy::ShareMesh) < 0) {
|
||||
MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs open the project file."),
|
||||
_L("Save project"), wxOK | wxICON_WARNING).ShowModal();
|
||||
return wxID_CANCEL;
|
||||
|
@ -7828,7 +7886,7 @@ int Plater::save_project(bool saveAs)
|
|||
up_to_date(true, true);
|
||||
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
p->dirty_state.reset_after_save();
|
||||
reset_project_dirty_after_save();
|
||||
return wxID_YES;
|
||||
}
|
||||
|
||||
|
@ -7839,9 +7897,9 @@ void Plater::import_model_id(const std::string& download_info)
|
|||
std::string filename;
|
||||
|
||||
std::string download_origin_url = wxGetApp().url_decode(download_info);
|
||||
fs::path download_path = fs::path(download_origin_url);
|
||||
download_url = download_origin_url;
|
||||
filename = download_path.filename().string();
|
||||
fs::path download_path = fs::path(download_origin_url);
|
||||
download_url = download_origin_url;
|
||||
filename = download_path.filename().string();
|
||||
|
||||
|
||||
bool download_ok = false;
|
||||
|
@ -7937,7 +7995,8 @@ void Plater::import_model_id(const std::string& download_info)
|
|||
}
|
||||
|
||||
//target_path /= (boost::format("%1%_%2%.3mf") % filename % unique).str();
|
||||
target_path /= filename.c_str();
|
||||
target_path /= fs::path(wxString(filename).wc_str());
|
||||
|
||||
fs::path tmp_path = target_path;
|
||||
tmp_path += format(".%1%", ".download");
|
||||
|
||||
|
@ -8048,11 +8107,11 @@ void Plater::add_model(bool imperial_units/* = false*/, std::string fname/* = "
|
|||
std::vector<fs::path> paths;
|
||||
|
||||
if(fname.empty()){
|
||||
wxArrayString input_files;
|
||||
wxArrayString input_files;
|
||||
wxGetApp().import_model(this, input_files);
|
||||
if (input_files.empty())
|
||||
return;
|
||||
|
||||
|
||||
for (const auto &file : input_files)
|
||||
paths.emplace_back(into_path(file));
|
||||
}
|
||||
|
@ -8659,6 +8718,16 @@ void Plater::load_gcode(const wxString& filename)
|
|||
}
|
||||
*current_result = std::move(processor.extract_result());
|
||||
//current_result->filename = filename;
|
||||
|
||||
BedType bed_type = current_result->bed_type;
|
||||
if (bed_type != BedType::btCount) {
|
||||
DynamicPrintConfig &proj_config = wxGetApp().preset_bundle->project_config;
|
||||
proj_config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type));
|
||||
on_bed_type_change(bed_type);
|
||||
}
|
||||
|
||||
current_print.apply(this->model(), wxGetApp().preset_bundle->full_config());
|
||||
|
||||
current_print.set_gcode_file_ready();
|
||||
|
||||
// show results
|
||||
|
@ -9402,6 +9471,9 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); }
|
|||
bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); }
|
||||
void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
|
||||
|
||||
bool Plater::is_view3D_overhang_shown() const { return p->is_view3D_overhang_shown(); }
|
||||
void Plater::show_view3D_overhang(bool show) { p->show_view3D_overhang(show); }
|
||||
|
||||
bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
|
||||
void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
|
||||
|
||||
|
@ -9446,8 +9518,12 @@ int GUI::Plater::close_with_confirm(std::function<bool(bool)> second_check)
|
|||
wxGetApp().app_config->set("save_project_choise", result == wxID_YES ? "yes" : "no");
|
||||
if (result == wxID_YES) {
|
||||
result = save_project();
|
||||
if (result == wxID_CANCEL)
|
||||
return result;
|
||||
if (result == wxID_CANCEL) {
|
||||
if (choise.empty())
|
||||
return result;
|
||||
else
|
||||
result = wxID_NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9938,38 +10014,92 @@ void Plater::export_core_3mf()
|
|||
export_3mf(path_u8, SaveStrategy::Silence);
|
||||
}
|
||||
|
||||
// Following lambda generates a combined mesh for export with normals pointing outwards.
|
||||
TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, std::function<void(const std::string&)> notify_func)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
|
||||
std::vector<csg::CSGPart> csgmesh;
|
||||
csgmesh.reserve(2 * mo.volumes.size());
|
||||
bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh),
|
||||
csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits);
|
||||
|
||||
if (csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }) == csgmesh.end()) {
|
||||
try {
|
||||
MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) });
|
||||
mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr);
|
||||
}
|
||||
catch (...) {}
|
||||
#if 0
|
||||
// if mcut fails, try again with CGAL
|
||||
if (mesh.empty()) {
|
||||
try {
|
||||
auto meshPtr = csg::perform_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) });
|
||||
mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*meshPtr);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mesh.empty()) {
|
||||
if (notify_func)
|
||||
notify_func(_u8L("Unable to perform boolean operation on model meshes. "
|
||||
"Only positive parts will be exported."));
|
||||
|
||||
for (const ModelVolume* v : mo.volumes)
|
||||
if (v->is_model_part()) {
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix(), true);
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (instance_id == -1) {
|
||||
TriangleMesh vols_mesh(mesh);
|
||||
mesh = TriangleMesh();
|
||||
for (const ModelInstance* i : mo.instances) {
|
||||
TriangleMesh m = vols_mesh;
|
||||
m.transform(i->get_matrix(), true);
|
||||
mesh.merge(m);
|
||||
}
|
||||
}
|
||||
else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
mesh.transform(mo.instances[instance_id]->get_matrix(), true);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// BBS export with/without boolean, however, stil merge mesh
|
||||
#define EXPORT_WITH_BOOLEAN 0
|
||||
void Plater::export_stl(bool extended, bool selection_only)
|
||||
{
|
||||
if (p->model.objects.empty()) { return; }
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
const auto &selection = p->get_selection();
|
||||
|
||||
// BBS support mulity objects
|
||||
// const auto obj_idx = selection.get_object_idx();
|
||||
// if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
|
||||
// return;
|
||||
|
||||
if (selection_only && selection.is_wipe_tower())
|
||||
return;
|
||||
|
||||
//BBS
|
||||
if (selection_only) {
|
||||
// only support selection single full object and mulitiple full object
|
||||
if (!selection.is_single_full_object() && !selection.is_multiple_full_object())
|
||||
return;
|
||||
}
|
||||
|
||||
wxString path = p->get_export_file(FT_STL);
|
||||
if (path.empty()) { return; }
|
||||
const std::string path_u8 = into_u8(path);
|
||||
|
||||
wxBusyCursor wait;
|
||||
const auto& selection = p->get_selection();
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
|
||||
#if EXPORT_WITH_BOOLEAN
|
||||
if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
|
||||
return;
|
||||
#else
|
||||
// BBS support selecting multiple objects
|
||||
if (selection_only && selection.is_wipe_tower()) return;
|
||||
|
||||
// BBS
|
||||
if (selection_only) {
|
||||
// only support selection single full object and mulitiple full object
|
||||
if (!selection.is_single_full_object() && !selection.is_multiple_full_object()) return;
|
||||
}
|
||||
|
||||
// Following lambda generates a combined mesh for export with normals pointing outwards.
|
||||
auto mesh_to_export = [](const ModelObject& mo, int instance_id) {
|
||||
auto mesh_to_export_fff_no_boolean = [](const ModelObject &mo, int instance_id) {
|
||||
TriangleMesh mesh;
|
||||
for (const ModelVolume* v : mo.volumes)
|
||||
for (const ModelVolume *v : mo.volumes)
|
||||
if (v->is_model_part()) {
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix(), true);
|
||||
|
@ -9978,125 +10108,72 @@ void Plater::export_stl(bool extended, bool selection_only)
|
|||
if (instance_id == -1) {
|
||||
TriangleMesh vols_mesh(mesh);
|
||||
mesh = TriangleMesh();
|
||||
for (const ModelInstance* i : mo.instances) {
|
||||
for (const ModelInstance *i : mo.instances) {
|
||||
TriangleMesh m = vols_mesh;
|
||||
m.transform(i->get_matrix(), true);
|
||||
mesh.merge(m);
|
||||
}
|
||||
}
|
||||
else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
} else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
mesh.transform(mo.instances[instance_id]->get_matrix(), true);
|
||||
return mesh;
|
||||
};
|
||||
#endif
|
||||
auto mesh_to_export_sla = [&, this](const ModelObject& mo, int instance_id) {
|
||||
TriangleMesh mesh;
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (p->printer_technology == ptFFF) {
|
||||
if (selection_only) {
|
||||
if (selection.is_single_full_object()) {
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
const ModelObject* model_object = p->model.objects[obj_idx];
|
||||
if (selection.get_mode() == Selection::Instance)
|
||||
{
|
||||
mesh = std::move(mesh_to_export(*model_object, ( model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()));
|
||||
}
|
||||
else
|
||||
{
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix(), true);
|
||||
}
|
||||
const SLAPrintObject *object = this->p->sla_print.get_print_object_by_model_object_id(mo.id());
|
||||
|
||||
if (model_object->instances.size() == 1)
|
||||
mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
else if (selection.is_multiple_full_object()) {
|
||||
const std::set<std::pair<int, int>>& instances_idxs = p->get_selection().get_selected_object_instances();
|
||||
for (const std::pair<int, int>& i : instances_idxs)
|
||||
{
|
||||
ModelObject* object = p->model.objects[i.first];
|
||||
mesh.merge(mesh_to_export(*object, i.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto m = object->get_mesh_to_print(); m.empty())
|
||||
mesh = combine_mesh_fff(mo, instance_id, [this](const std::string& msg) {return get_notification_manager()->push_plater_error_notification(msg); });
|
||||
else {
|
||||
for (const ModelObject *o : p->model.objects)
|
||||
mesh.merge(mesh_to_export(*o, -1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is SLA mode, all objects have only one volume.
|
||||
// However, we must have a look at the backend to load
|
||||
// hollowed mesh and/or supports
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
const PrintObjects& objects = p->sla_print.objects();
|
||||
for (const SLAPrintObject* object : objects)
|
||||
{
|
||||
const ModelObject* model_object = object->model_object();
|
||||
if (selection_only) {
|
||||
if (model_object->id() != p->model.objects[obj_idx]->id())
|
||||
continue;
|
||||
}
|
||||
Transform3d mesh_trafo_inv = object->trafo().inverse();
|
||||
bool is_left_handed = object->is_left_handed();
|
||||
const Transform3d mesh_trafo_inv = object->trafo().inverse();
|
||||
const bool is_left_handed = object->is_left_handed();
|
||||
|
||||
TriangleMesh pad_mesh;
|
||||
bool has_pad_mesh = extended && object->has_mesh(slaposPad);
|
||||
if (has_pad_mesh)
|
||||
{
|
||||
pad_mesh = object->get_mesh(slaposPad);
|
||||
pad_mesh.transform(mesh_trafo_inv);
|
||||
}
|
||||
auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{};
|
||||
pad_mesh.transform(mesh_trafo_inv);
|
||||
|
||||
auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{};
|
||||
supports_mesh.transform(mesh_trafo_inv);
|
||||
|
||||
TriangleMesh supports_mesh;
|
||||
bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree);
|
||||
if (has_supports_mesh)
|
||||
{
|
||||
supports_mesh = object->get_mesh(slaposSupportTree);
|
||||
supports_mesh.transform(mesh_trafo_inv);
|
||||
}
|
||||
const std::vector<SLAPrintObject::Instance>& obj_instances = object->instances();
|
||||
for (const SLAPrintObject::Instance& obj_instance : obj_instances)
|
||||
{
|
||||
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
|
||||
[&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; });
|
||||
assert(it != model_object->instances.end());
|
||||
for (const SLAPrintObject::Instance& obj_instance : obj_instances) {
|
||||
auto it = std::find_if(object->model_object()->instances.begin(), object->model_object()->instances.end(),
|
||||
[&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; });
|
||||
assert(it != object->model_object()->instances.end());
|
||||
|
||||
if (it != model_object->instances.end())
|
||||
{
|
||||
bool one_inst_only = selection_only && ! selection.is_single_full_object();
|
||||
if (it != object->model_object()->instances.end()) {
|
||||
const bool one_inst_only = selection_only && ! selection.is_single_full_object();
|
||||
|
||||
int instance_idx = it - model_object->instances.begin();
|
||||
const int instance_idx = it - object->model_object()->instances.begin();
|
||||
const Transform3d& inst_transform = one_inst_only
|
||||
? Transform3d::Identity()
|
||||
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
|
||||
? Transform3d::Identity()
|
||||
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
|
||||
|
||||
TriangleMesh inst_mesh;
|
||||
|
||||
if (has_pad_mesh)
|
||||
{
|
||||
if (!pad_mesh.empty()) {
|
||||
TriangleMesh inst_pad_mesh = pad_mesh;
|
||||
inst_pad_mesh.transform(inst_transform, is_left_handed);
|
||||
inst_mesh.merge(inst_pad_mesh);
|
||||
}
|
||||
|
||||
if (has_supports_mesh)
|
||||
{
|
||||
if (!supports_mesh.empty()) {
|
||||
TriangleMesh inst_supports_mesh = supports_mesh;
|
||||
inst_supports_mesh.transform(inst_transform, is_left_handed);
|
||||
inst_mesh.merge(inst_supports_mesh);
|
||||
}
|
||||
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_slice();
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_print();
|
||||
|
||||
inst_object_mesh.transform(mesh_trafo_inv);
|
||||
inst_object_mesh.transform(inst_transform, is_left_handed);
|
||||
|
||||
inst_mesh.merge(inst_object_mesh);
|
||||
|
||||
// ensure that the instance lays on the bed
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min[2]);
|
||||
// ensure that the instance lays on the bed
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z());
|
||||
|
||||
// merge instance with global mesh
|
||||
// merge instance with global mesh
|
||||
mesh.merge(inst_mesh);
|
||||
|
||||
if (one_inst_only)
|
||||
|
@ -10104,6 +10181,50 @@ void Plater::export_stl(bool extended, bool selection_only)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
};
|
||||
|
||||
std::function<TriangleMesh(const ModelObject& mo, int instance_id)>
|
||||
mesh_to_export;
|
||||
|
||||
if (p->printer_technology == ptFFF)
|
||||
#if EXPORT_WITH_BOOLEAN
|
||||
mesh_to_export = [this](const ModelObject& mo, int instance_id) {return Plater::combine_mesh_fff(mo, instance_id,
|
||||
[this](const std::string& msg) {return get_notification_manager()->push_plater_error_notification(msg); }); };
|
||||
#else
|
||||
mesh_to_export = mesh_to_export_fff_no_boolean;
|
||||
#endif
|
||||
else
|
||||
mesh_to_export = mesh_to_export_sla;
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (selection_only) {
|
||||
if (selection.is_single_full_object()) {
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
const ModelObject* model_object = p->model.objects[obj_idx];
|
||||
if (selection.get_mode() == Selection::Instance)
|
||||
mesh = mesh_to_export(*model_object, (model_object->instances.size() > 1) ? -1 : selection.get_instance_idx());
|
||||
else {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix(), true);
|
||||
}
|
||||
|
||||
if (model_object->instances.size() == 1) mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
else if (selection.is_multiple_full_object()) {
|
||||
const std::set<std::pair<int, int>>& instances_idxs = p->get_selection().get_selected_object_instances();
|
||||
for (const std::pair<int, int>& i : instances_idxs) {
|
||||
ModelObject* object = p->model.objects[i.first];
|
||||
mesh.merge(mesh_to_export(*object, i.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const ModelObject* o : p->model.objects) {
|
||||
mesh.merge(mesh_to_export(*o, -1));
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
|
||||
|
@ -11038,7 +11159,7 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
|
|||
|
||||
return colors;
|
||||
}
|
||||
|
||||
|
||||
wxString Plater::get_project_filename(const wxString& extension) const
|
||||
{
|
||||
return p->get_project_filename(extension);
|
||||
|
@ -11469,7 +11590,8 @@ int Plater::select_plate(int plate_index, bool need_slice)
|
|||
{
|
||||
if (need_slice) { //from preview's thumbnail
|
||||
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) || (gcode_result->moves.empty())){
|
||||
//part_plate->update_slice_result_valid_state(false);
|
||||
if (invalidated & PrintBase::APPLY_STATUS_INVALIDATED)
|
||||
part_plate->update_slice_result_valid_state(false);
|
||||
p->process_completed_with_error = -1;
|
||||
p->m_slice_all = false;
|
||||
reset_gcode_toolpaths();
|
||||
|
@ -11897,11 +12019,23 @@ void Plater::show_object_info()
|
|||
{
|
||||
NotificationManager *notify_manager = get_notification_manager();
|
||||
const Selection& selection = get_selection();
|
||||
int selCount = selection.get_volume_idxs().size();
|
||||
ModelObjectPtrs objects = model().objects;
|
||||
int obj_idx = selection.get_object_idx();
|
||||
std::string info_text;
|
||||
|
||||
if (objects.empty() || (obj_idx < 0) || (obj_idx >= objects.size()) ||
|
||||
if (selCount > 1 && !selection.is_single_full_object()) {
|
||||
notify_manager->bbl_close_objectsinfo_notification();
|
||||
if (selection.get_mode() == Selection::EMode::Volume) {
|
||||
info_text += (boost::format(_utf8(L("Number of currently selected parts: %1%\n"))) % selCount).str();
|
||||
} else if (selection.get_mode() == Selection::EMode::Instance) {
|
||||
int content_count = selection.get_content().size();
|
||||
info_text += (boost::format(_utf8(L("Number of currently selected objects: %1%\n"))) % content_count).str();
|
||||
}
|
||||
notify_manager->bbl_show_objectsinfo_notification(info_text, false, !(p->current_panel == p->view3D));
|
||||
return;
|
||||
}
|
||||
else if (objects.empty() || (obj_idx < 0) || (obj_idx >= objects.size()) ||
|
||||
objects[obj_idx]->volumes.empty() ||// hack to avoid crash when deleting the last object on the bed
|
||||
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
|
||||
!(selection.is_single_full_instance() || selection.is_single_volume()))
|
||||
|
@ -11939,12 +12073,12 @@ void Plater::show_object_info()
|
|||
size = vol->get_convex_hull().transformed_bounding_box(t).size();
|
||||
}
|
||||
else {
|
||||
int obj_idx, vol_idx;
|
||||
wxGetApp().obj_list()->get_selected_item_indexes(obj_idx, vol_idx);
|
||||
if (obj_idx < 0) {
|
||||
//corner case when merge/split/remove
|
||||
return;
|
||||
}
|
||||
//int obj_idx, vol_idx;
|
||||
//wxGetApp().obj_list()->get_selected_item_indexes(obj_idx, vol_idx);
|
||||
//if (obj_idx < 0) {
|
||||
// //corner case when merge/split/remove
|
||||
// return;
|
||||
//}
|
||||
info_text += (boost::format(_utf8(L("Object name: %1%\n"))) % model_object->name).str();
|
||||
face_count = static_cast<int>(model_object->facets_count());
|
||||
size = model_object->instance_convex_hull_bounding_box(inst_idx).size();
|
||||
|
@ -12277,7 +12411,8 @@ wxMenu* Plater::default_menu() { return p->menus.default_menu();
|
|||
wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); }
|
||||
wxMenu* Plater::layer_menu() { return p->menus.layer_menu(); }
|
||||
wxMenu* Plater::multi_selection_menu() { return p->menus.multi_selection_menu(); }
|
||||
|
||||
int Plater::GetPlateIndexByRightMenuInLeftUI() { return p->m_is_RightClickInLeftUI; }
|
||||
void Plater::SetPlateIndexByRightMenuInLeftUI(int index) { p->m_is_RightClickInLeftUI = index; }
|
||||
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
|
||||
m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled())
|
||||
{
|
||||
|
|
|
@ -288,6 +288,9 @@ public:
|
|||
bool are_view3D_labels_shown() const;
|
||||
void show_view3D_labels(bool show);
|
||||
|
||||
bool is_view3D_overhang_shown() const;
|
||||
void show_view3D_overhang(bool show);
|
||||
|
||||
bool is_sidebar_collapsed() const;
|
||||
void collapse_sidebar(bool show);
|
||||
|
||||
|
@ -332,6 +335,7 @@ public:
|
|||
void export_gcode_3mf(bool export_all = false);
|
||||
void send_gcode_finish(wxString name);
|
||||
void export_core_3mf();
|
||||
static TriangleMesh combine_mesh_fff(const ModelObject& mo, int instance_id, std::function<void(const std::string&)> notify_func = {});
|
||||
void export_stl(bool extended = false, bool selection_only = false);
|
||||
//BBS: remove amf
|
||||
//void export_amf();
|
||||
|
@ -686,7 +690,8 @@ public:
|
|||
wxMenu* instance_menu();
|
||||
wxMenu* layer_menu();
|
||||
wxMenu* multi_selection_menu();
|
||||
|
||||
int GetPlateIndexByRightMenuInLeftUI();
|
||||
void SetPlateIndexByRightMenuInLeftUI(int);
|
||||
static bool has_illegal_filename_characters(const wxString& name);
|
||||
static bool has_illegal_filename_characters(const std::string& name);
|
||||
static void show_illegal_characters_warning(wxWindow* parent);
|
||||
|
|
|
@ -1961,7 +1961,9 @@ void Selection::update_type()
|
|||
unsigned int sels_cntr = 0;
|
||||
for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it)
|
||||
{
|
||||
const ModelObject* model_object = m_model->objects[it->first];
|
||||
bool is_wipe_tower = it->first >= 1000;
|
||||
int actual_obj_id = is_wipe_tower ? it->first - 1000 : it->first;
|
||||
const ModelObject *model_object = m_model->objects[actual_obj_id];
|
||||
unsigned int volumes_count = (unsigned int)model_object->volumes.size();
|
||||
unsigned int instances_count = (unsigned int)model_object->instances.size();
|
||||
sels_cntr += volumes_count * instances_count;
|
||||
|
@ -2710,9 +2712,14 @@ void Selection::paste_objects_from_clipboard()
|
|||
displacement = {empty_cell.x() + point_offset.x(), empty_cell.y() + point_offset.y(), start_offset(2)};
|
||||
}
|
||||
|
||||
for (ModelInstance* inst : dst_object->instances)
|
||||
for (ModelInstance* inst : dst_object->instances) {
|
||||
inst->set_offset(displacement);
|
||||
|
||||
//BBS init asssmble transformation
|
||||
Geometry::Transformation t = inst->get_transformation();
|
||||
inst->set_assemble_transformation(t);
|
||||
}
|
||||
|
||||
object_idxs.push_back(m_model->objects.size() - 1);
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
|
|
|
@ -362,6 +362,11 @@ public:
|
|||
|
||||
bool requires_local_axes() const;
|
||||
|
||||
void render_bounding_box(const BoundingBoxf3& box, float* color, float scale) {
|
||||
m_scale_factor = scale;
|
||||
render_bounding_box(box, color);
|
||||
}
|
||||
|
||||
//BBS
|
||||
void cut_to_clipboard();
|
||||
void copy_to_clipboard();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue