mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-18 22:31:13 -06:00
Port Emboss & SVG gizmo from PrusaSlicer (#2819)
* Rework UI jobs to make them more understandable and flexible. * Update Orca specific jobs * Fix progress issue * Fix dark mode and window radius * Update cereal version from 1.2.2 to 1.3.0 (cherry picked from commit prusa3d/PrusaSlicer@057232a275) * Initial port of Emboss gizmo * Bump up CGAL version to 5.4 (cherry picked from commit prusa3d/PrusaSlicer@1bf9dee3e7) * Fix text rotation * Fix test dragging * Add text gizmo to right click menu * Initial port of SVG gizmo * Fix text rotation * Fix Linux build * Fix "from surface" * Fix -90 rotation * Fix icon path * Fix loading font with non-ascii name * Fix storing non-utf8 font descriptor in 3mf file * Fix filtering with non-utf8 characters * Emboss: Use Orca style input dialog * Fix build on macOS * Fix tooltip color in light mode * InputText: fixed incorrect padding when FrameBorder > 0. (ocornut/imgui#4794, ocornut/imgui#3781) InputTextMultiline: fixed vertical tracking with large values of FramePadding.y. (ocornut/imgui#3781, ocornut/imgui#4794) (cherry picked from commit ocornut/imgui@072caa4a90) (cherry picked from commit ocornut/imgui@bdd2a94315) * SVG: Use Orca style input dialog * Fix job progress update * Fix crash when select editing text in preview screen * Use Orca checkbox style * Fix issue that toolbar icons are kept regenerated * Emboss: Fix text & icon alignment * SVG: Fix text & icon alignment * Emboss: fix toolbar icon mouse hover state * Add a simple subtle outline effect by drawing back faces using wireframe mode * Disable selection outlines * Show outline in white if the model color is too dark * Make the outline algorithm more reliable * Enable cull face, which fix render on Linux * Fix `disable_cullface` * Post merge fix * Optimize selection rendering * Fix scale gizmo * Emboss: Fix text rotation if base object is scaled * Fix volume synchronize * Fix emboss rotation * Emboss: Fix advance toggle * Fix text position after reopened the project * Make font style preview darker * Make font style preview selector height shorter --------- Co-authored-by: tamasmeszaros <meszaros.q@gmail.com> Co-authored-by: ocornut <omarcornut@gmail.com> Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
7a8e1929ee
commit
933aa3050b
197 changed files with 27190 additions and 2454 deletions
|
@ -124,6 +124,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Gizmos/GLGizmosCommon.hpp
|
||||
GUI/Gizmos/GLGizmoBase.cpp
|
||||
GUI/Gizmos/GLGizmoBase.hpp
|
||||
GUI/Gizmos/GLGizmoEmboss.cpp
|
||||
GUI/Gizmos/GLGizmoEmboss.hpp
|
||||
GUI/Gizmos/GLGizmoMove.cpp
|
||||
GUI/Gizmos/GLGizmoMove.hpp
|
||||
GUI/Gizmos/GLGizmoRotate.cpp
|
||||
|
@ -144,6 +146,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Gizmos/GLGizmoPainterBase.hpp
|
||||
GUI/Gizmos/GLGizmoSimplify.cpp
|
||||
GUI/Gizmos/GLGizmoSimplify.hpp
|
||||
GUI/Gizmos/GLGizmoSVG.cpp
|
||||
GUI/Gizmos/GLGizmoSVG.hpp
|
||||
GUI/Gizmos/GLGizmoMmuSegmentation.cpp
|
||||
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
|
||||
#GUI/Gizmos/GLGizmoFaceDetector.cpp
|
||||
|
@ -152,8 +156,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Gizmos/GLGizmoMeasure.hpp
|
||||
GUI/Gizmos/GLGizmoSeam.cpp
|
||||
GUI/Gizmos/GLGizmoSeam.hpp
|
||||
GUI/Gizmos/GLGizmoText.cpp
|
||||
GUI/Gizmos/GLGizmoText.hpp
|
||||
#GUI/Gizmos/GLGizmoText.cpp
|
||||
#GUI/Gizmos/GLGizmoText.hpp
|
||||
GUI/Gizmos/GLGizmoMeshBoolean.cpp
|
||||
GUI/Gizmos/GLGizmoMeshBoolean.hpp
|
||||
GUI/GLSelectionRectangle.cpp
|
||||
|
@ -194,6 +198,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/GUI_Geometry.hpp
|
||||
GUI/I18N.cpp
|
||||
GUI/I18N.hpp
|
||||
GUI/IconManager.cpp
|
||||
GUI/IconManager.hpp
|
||||
GUI/MainFrame.cpp
|
||||
GUI/MainFrame.hpp
|
||||
GUI/BBLTopbar.cpp
|
||||
|
@ -303,6 +309,10 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/RemovableDriveManager.hpp
|
||||
GUI/SendSystemInfoDialog.cpp
|
||||
GUI/SendSystemInfoDialog.hpp
|
||||
GUI/SurfaceDrag.cpp
|
||||
GUI/SurfaceDrag.hpp
|
||||
GUI/TextLines.cpp
|
||||
GUI/TextLines.hpp
|
||||
GUI/PlateSettingsDialog.cpp
|
||||
GUI/PlateSettingsDialog.hpp
|
||||
GUI/ImGuiWrapper.hpp
|
||||
|
@ -329,13 +339,21 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/UpdateDialogs.cpp
|
||||
GUI/UpdateDialogs.hpp
|
||||
GUI/Jobs/Job.hpp
|
||||
GUI/Jobs/Job.cpp
|
||||
GUI/Jobs/PlaterJob.hpp
|
||||
GUI/Jobs/PlaterJob.cpp
|
||||
GUI/Jobs/Worker.hpp
|
||||
GUI/Jobs/BoostThreadWorker.hpp
|
||||
GUI/Jobs/BoostThreadWorker.cpp
|
||||
GUI/Jobs/BusyCursorJob.hpp
|
||||
GUI/Jobs/PlaterWorker.hpp
|
||||
GUI/Jobs/UpgradeNetworkJob.hpp
|
||||
GUI/Jobs/UpgradeNetworkJob.cpp
|
||||
GUI/Jobs/ArrangeJob.hpp
|
||||
GUI/Jobs/ArrangeJob.cpp
|
||||
GUI/Jobs/CreateFontNameImageJob.cpp
|
||||
GUI/Jobs/CreateFontNameImageJob.hpp
|
||||
GUI/Jobs/CreateFontStyleImagesJob.cpp
|
||||
GUI/Jobs/CreateFontStyleImagesJob.hpp
|
||||
GUI/Jobs/EmbossJob.cpp
|
||||
GUI/Jobs/EmbossJob.hpp
|
||||
GUI/Jobs/OrientJob.hpp
|
||||
GUI/Jobs/OrientJob.cpp
|
||||
GUI/Jobs/RotoptimizeJob.hpp
|
||||
|
@ -353,6 +371,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Jobs/BindJob.cpp
|
||||
GUI/Jobs/NotificationProgressIndicator.hpp
|
||||
GUI/Jobs/NotificationProgressIndicator.cpp
|
||||
GUI/Jobs/ThreadSafeQueue.hpp
|
||||
GUI/Jobs/SLAImportDialog.hpp
|
||||
GUI/PhysicalPrinterDialog.hpp
|
||||
GUI/PhysicalPrinterDialog.cpp
|
||||
GUI/ProgressStatusBar.hpp
|
||||
|
@ -449,6 +469,10 @@ set(SLIC3R_GUI_SOURCES
|
|||
Utils/Http.hpp
|
||||
Utils/FixModelByWin10.cpp
|
||||
Utils/FixModelByWin10.hpp
|
||||
Utils/EmbossStyleManager.cpp
|
||||
Utils/EmbossStyleManager.hpp
|
||||
Utils/FontConfigHelp.cpp
|
||||
Utils/FontConfigHelp.hpp
|
||||
Utils/Bonjour.cpp
|
||||
Utils/Bonjour.hpp
|
||||
Utils/FileHelp.cpp
|
||||
|
@ -457,6 +481,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
Utils/PresetUpdater.hpp
|
||||
Utils/Process.cpp
|
||||
Utils/Process.hpp
|
||||
Utils/RaycastManager.cpp
|
||||
Utils/RaycastManager.hpp
|
||||
Utils/Profile.hpp
|
||||
Utils/UndoRedo.cpp
|
||||
Utils/UndoRedo.hpp
|
||||
|
@ -480,8 +506,10 @@ set(SLIC3R_GUI_SOURCES
|
|||
Utils/PrintHost.hpp
|
||||
Utils/Serial.cpp
|
||||
Utils/Serial.hpp
|
||||
Utils/MKS.hpp
|
||||
Utils/MKS.cpp
|
||||
Utils/MKS.hpp
|
||||
Utils/WxFontUtils.cpp
|
||||
Utils/WxFontUtils.hpp
|
||||
Utils/Duet.cpp
|
||||
Utils/Duet.hpp
|
||||
Utils/FlashAir.cpp
|
||||
|
@ -591,7 +619,7 @@ endif ()
|
|||
if (UNIX AND NOT APPLE)
|
||||
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
||||
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
|
||||
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES})
|
||||
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig)
|
||||
|
||||
# We add GStreamer for bambu:/// support.
|
||||
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
|
||||
|
|
|
@ -940,9 +940,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
|
||||
shader->set_uniform("view_normal_matrix", view_normal_matrix);
|
||||
//BBS: add outline related logic
|
||||
if (with_outline && volume.first->selected)
|
||||
volume.first->render_with_outline(view_matrix * model_matrix);
|
||||
else
|
||||
//if (with_outline && volume.first->selected)
|
||||
// volume.first->render_with_outline(view_matrix * model_matrix);
|
||||
//else
|
||||
volume.first->render();
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
|
|
|
@ -243,14 +243,15 @@ public:
|
|||
|
||||
const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; }
|
||||
void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_transformation(const Transform3d& transform) { m_instance_transformation.set_matrix(transform); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); }
|
||||
Vec3d get_instance_offset() const { return m_instance_transformation.get_offset(); }
|
||||
double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); }
|
||||
|
||||
void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); }
|
||||
Vec3d get_instance_rotation() const { return m_instance_transformation.get_rotation(); }
|
||||
double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); }
|
||||
|
||||
void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
|
||||
|
@ -262,7 +263,7 @@ public:
|
|||
void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); }
|
||||
Vec3d get_instance_mirror() const { return m_instance_transformation.get_mirror(); }
|
||||
double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); }
|
||||
|
||||
void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
|
||||
|
@ -270,26 +271,27 @@ public:
|
|||
|
||||
const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; }
|
||||
void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_transformation(const Transform3d& transform) { m_volume_transformation.set_matrix(transform); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); }
|
||||
Vec3d get_volume_offset() const { return m_volume_transformation.get_offset(); }
|
||||
double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); }
|
||||
|
||||
void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); }
|
||||
Vec3d get_volume_rotation() const { return m_volume_transformation.get_rotation(); }
|
||||
double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); }
|
||||
|
||||
void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); }
|
||||
Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); }
|
||||
double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
|
||||
|
||||
const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); }
|
||||
Vec3d get_volume_mirror() const { return m_volume_transformation.get_mirror(); }
|
||||
double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); }
|
||||
|
||||
void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
#include "Widgets/WebView.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -374,6 +376,8 @@ wxString get_fail_reason(int code)
|
|||
|
||||
m_status_bar = std::make_shared<BBLStatusBarBind>(m_simplebook);
|
||||
|
||||
m_worker = std::make_unique<PlaterWorker<BoostThreadWorker>>(this, m_status_bar, "bind_worker");
|
||||
|
||||
auto button_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, BIND_DIALOG_BUTTON_PANEL_SIZE);
|
||||
button_panel->SetBackgroundColour(*wxWHITE);
|
||||
wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
@ -513,10 +517,7 @@ wxString get_fail_reason(int code)
|
|||
|
||||
void BindMachineDialog::on_destroy()
|
||||
{
|
||||
if (m_bind_job) {
|
||||
m_bind_job->cancel();
|
||||
m_bind_job->join();
|
||||
}
|
||||
m_worker.get()->cancel_all();
|
||||
}
|
||||
|
||||
void BindMachineDialog::on_close(wxCloseEvent &event)
|
||||
|
@ -572,7 +573,7 @@ wxString get_fail_reason(int code)
|
|||
agent->track_update_property("dev_ota_version", m_machine_info->get_ota_version());
|
||||
|
||||
m_simplebook->SetSelection(0);
|
||||
m_bind_job = std::make_shared<BindJob>(m_status_bar, wxGetApp().plater(), m_machine_info->dev_id, m_machine_info->dev_ip, m_machine_info->bind_sec_link);
|
||||
auto m_bind_job = std::make_unique<BindJob>(m_machine_info->dev_id, m_machine_info->dev_ip, m_machine_info->bind_sec_link);
|
||||
|
||||
if (m_machine_info && (m_machine_info->get_printer_series() == PrinterSeries::SERIES_X1)) {
|
||||
m_bind_job->set_improved(false);
|
||||
|
@ -582,7 +583,7 @@ wxString get_fail_reason(int code)
|
|||
}
|
||||
|
||||
m_bind_job->set_event_handle(this);
|
||||
m_bind_job->start();
|
||||
replace_job(*m_worker, std::move(m_bind_job));
|
||||
}
|
||||
|
||||
void BindMachineDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "Jobs/BindJob.hpp"
|
||||
#include "BBLStatusBar.hpp"
|
||||
#include "BBLStatusBarBind.hpp"
|
||||
#include "Jobs/Worker.hpp"
|
||||
|
||||
#define BIND_DIALOG_GREY200 wxColour(248, 248, 248)
|
||||
#define BIND_DIALOG_GREY800 wxColour(50, 58, 61)
|
||||
|
@ -77,8 +78,8 @@ private:
|
|||
std::shared_ptr<int> m_tocken;
|
||||
|
||||
MachineObject * m_machine_info{nullptr};
|
||||
std::shared_ptr<BindJob> m_bind_job;
|
||||
std::shared_ptr<BBLStatusBarBind> m_status_bar;
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
|
||||
public:
|
||||
BindMachineDialog(Plater *plater = nullptr);
|
||||
|
|
|
@ -1437,12 +1437,10 @@ void CalibrationPresetPage::on_cali_finished_job()
|
|||
void CalibrationPresetPage::on_cali_cancel_job()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "CalibrationWizard::print_job: enter canceled";
|
||||
if (CalibUtils::print_job) {
|
||||
if (CalibUtils::print_job->is_running()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "calibration_print_job: canceled";
|
||||
CalibUtils::print_job->cancel();
|
||||
}
|
||||
CalibUtils::print_job->join();
|
||||
if (CalibUtils::print_worker) {
|
||||
BOOST_LOG_TRIVIAL(info) << "calibration_print_job: canceled";
|
||||
CalibUtils::print_worker->cancel_all();
|
||||
CalibUtils::print_worker->wait_for_idle();
|
||||
}
|
||||
|
||||
m_sending_panel->reset();
|
||||
|
|
|
@ -1396,12 +1396,10 @@ void CalibrationFlowCoarseSavePage::on_cali_finished_job()
|
|||
void CalibrationFlowCoarseSavePage::on_cali_cancel_job()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "CalibrationWizard::print_job: enter canceled";
|
||||
if (CalibUtils::print_job) {
|
||||
if (CalibUtils::print_job->is_running()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "calibration_print_job: canceled";
|
||||
CalibUtils::print_job->cancel();
|
||||
}
|
||||
CalibUtils::print_job->join();
|
||||
if (CalibUtils::print_worker) {
|
||||
BOOST_LOG_TRIVIAL(info) << "calibration_print_job: canceled";
|
||||
CalibUtils::print_worker->cancel_all();
|
||||
CalibUtils::print_worker->wait_for_idle();
|
||||
}
|
||||
|
||||
m_sending_panel->reset();
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "wxExtensions.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
|
||||
#define DESIGN_INPUT_SIZE wxSize(FromDIP(100), -1)
|
||||
|
||||
|
@ -59,7 +61,8 @@ DownloadProgressDialog::DownloadProgressDialog(wxString title)
|
|||
m_panel_download->SetSize(wxSize(FromDIP(400), FromDIP(70)));
|
||||
m_panel_download->SetMinSize(wxSize(FromDIP(400), FromDIP(70)));
|
||||
m_panel_download->SetMaxSize(wxSize(FromDIP(400), FromDIP(70)));
|
||||
|
||||
|
||||
m_worker = std::make_unique<PlaterWorker<BoostThreadWorker>>(this, m_status_bar, "download_worker");
|
||||
|
||||
//mode Download Failed
|
||||
auto m_panel_download_failed = new wxPanel(m_simplebook_status, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
@ -144,7 +147,7 @@ bool DownloadProgressDialog::Show(bool show)
|
|||
{
|
||||
if (show) {
|
||||
m_simplebook_status->SetSelection(0);
|
||||
m_upgrade_job = make_job(m_status_bar);
|
||||
auto m_upgrade_job = make_job();
|
||||
m_upgrade_job->set_event_handle(this);
|
||||
m_status_bar->set_progress(0);
|
||||
Bind(EVT_UPGRADE_NETWORK_SUCCESS, [this](wxCommandEvent& evt) {
|
||||
|
@ -182,23 +185,17 @@ bool DownloadProgressDialog::Show(bool show)
|
|||
});
|
||||
|
||||
m_status_bar->set_cancel_callback_fina([this]() {
|
||||
if (m_upgrade_job) {
|
||||
m_upgrade_job->cancel();
|
||||
//EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
m_worker->cancel_all();
|
||||
});
|
||||
m_upgrade_job->start();
|
||||
|
||||
replace_job(*m_worker, std::move(m_upgrade_job));
|
||||
}
|
||||
return DPIDialog::Show(show);
|
||||
}
|
||||
|
||||
void DownloadProgressDialog::on_close(wxCloseEvent& event)
|
||||
{
|
||||
if (m_upgrade_job) {
|
||||
m_upgrade_job->cancel();
|
||||
m_upgrade_job->join();
|
||||
}
|
||||
m_worker.get()->cancel_all();
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
@ -208,7 +205,7 @@ void DownloadProgressDialog::on_dpi_changed(const wxRect &suggested_rect) {}
|
|||
|
||||
void DownloadProgressDialog::update_release_note(std::string release_note, std::string version) {}
|
||||
|
||||
std::shared_ptr<UpgradeNetworkJob> DownloadProgressDialog::make_job(std::shared_ptr<ProgressIndicator> pri) { return std::make_shared<UpgradeNetworkJob>(pri); }
|
||||
std::unique_ptr<UpgradeNetworkJob> DownloadProgressDialog::make_job() { return std::make_unique<UpgradeNetworkJob>(); }
|
||||
|
||||
void DownloadProgressDialog::on_finish() { wxGetApp().restart_networking(); }
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Widgets/Button.hpp"
|
||||
#include "BBLStatusBar.hpp"
|
||||
#include "BBLStatusBarSend.hpp"
|
||||
#include "Jobs/Worker.hpp"
|
||||
#include "Jobs/UpgradeNetworkJob.hpp"
|
||||
|
||||
class wxBoxSizer;
|
||||
|
@ -47,11 +48,11 @@ public:
|
|||
wxSimplebook* m_simplebook_status{nullptr};
|
||||
|
||||
std::shared_ptr<BBLStatusBarSend> m_status_bar;
|
||||
std::shared_ptr<UpgradeNetworkJob> m_upgrade_job { nullptr };
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
wxPanel * m_panel_download;
|
||||
|
||||
protected:
|
||||
virtual std::shared_ptr<UpgradeNetworkJob> make_job(std::shared_ptr<ProgressIndicator> pri);
|
||||
virtual std::unique_ptr<UpgradeNetworkJob> make_job();
|
||||
virtual void on_finish();
|
||||
};
|
||||
|
||||
|
|
|
@ -361,7 +361,7 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he
|
|||
std::string layer_time = ImGui::ColorMarkerStart + _u8L("Layer Time: ") + ImGui::ColorMarkerEnd;
|
||||
std::string fanspeed = ImGui::ColorMarkerStart + _u8L("Fan: ") + ImGui::ColorMarkerEnd;
|
||||
std::string temperature = ImGui::ColorMarkerStart + _u8L("Temperature: ") + ImGui::ColorMarkerEnd;
|
||||
const float item_size = imgui.calc_text_size("X: 000.000 ").x;
|
||||
const float item_size = imgui.calc_text_size(std::string_view{"X: 000.000 "}).x;
|
||||
const float item_spacing = imgui.get_item_spacing().x;
|
||||
const float window_padding = ImGui::GetStyle().WindowPadding.x;
|
||||
|
||||
|
|
|
@ -1588,7 +1588,6 @@ void GLCanvas3D::enable_legend_texture(bool enable)
|
|||
void GLCanvas3D::enable_picking(bool enable)
|
||||
{
|
||||
m_picking_enabled = enable;
|
||||
m_selection.set_mode(Selection::Instance);
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_moving(bool enable)
|
||||
|
@ -2171,7 +2170,17 @@ std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
|
|||
|
||||
void GLCanvas3D::mirror_selection(Axis axis)
|
||||
{
|
||||
m_selection.mirror(axis);
|
||||
TransformationType transformation_type;
|
||||
if (wxGetApp().obj_manipul()->is_local_coordinates())
|
||||
transformation_type.set_local();
|
||||
else if (wxGetApp().obj_manipul()->is_instance_coordinates())
|
||||
transformation_type.set_instance();
|
||||
|
||||
transformation_type.set_relative();
|
||||
|
||||
m_selection.setup_cache();
|
||||
m_selection.mirror(axis, transformation_type);
|
||||
|
||||
do_mirror(L("Mirror Object"));
|
||||
// BBS
|
||||
//wxGetApp().obj_manipul()->set_dirty();
|
||||
|
@ -3364,7 +3373,9 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
else
|
||||
displacement = multiplier * direction;
|
||||
|
||||
m_selection.translate(displacement);
|
||||
TransformationType trafo_type;
|
||||
trafo_type.set_relative();
|
||||
m_selection.translate(displacement, trafo_type);
|
||||
m_dirty = true;
|
||||
}
|
||||
);}
|
||||
|
@ -4136,7 +4147,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
}
|
||||
|
||||
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D);
|
||||
TransformationType trafo_type;
|
||||
trafo_type.set_relative();
|
||||
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
|
||||
if (current_printer_technology() == ptFFF && (fff_print()->config().print_sequence == PrintSequence::ByObject))
|
||||
update_sequential_clearance();
|
||||
// BBS
|
||||
|
@ -4364,6 +4377,40 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
else
|
||||
evt.Skip();
|
||||
|
||||
// Detection of doubleclick on text to open emboss edit window
|
||||
auto type = m_gizmos.get_current_type();
|
||||
if (evt.LeftDClick() && !m_hover_volume_idxs.empty() &&
|
||||
(type == GLGizmosManager::EType::Undefined ||
|
||||
type == GLGizmosManager::EType::Move ||
|
||||
type == GLGizmosManager::EType::Rotate ||
|
||||
type == GLGizmosManager::EType::Scale ||
|
||||
type == GLGizmosManager::EType::Emboss ||
|
||||
type == GLGizmosManager::EType::Svg) ) {
|
||||
for (int hover_volume_id : m_hover_volume_idxs) {
|
||||
const GLVolume &hover_gl_volume = *m_volumes.volumes[hover_volume_id];
|
||||
int object_idx = hover_gl_volume.object_idx();
|
||||
if (object_idx < 0 || static_cast<size_t>(object_idx) >= m_model->objects.size()) continue;
|
||||
const ModelObject* hover_object = m_model->objects[object_idx];
|
||||
int hover_volume_idx = hover_gl_volume.volume_idx();
|
||||
if (hover_volume_idx < 0 || static_cast<size_t>(hover_volume_idx) >= hover_object->volumes.size()) continue;
|
||||
const ModelVolume* hover_volume = hover_object->volumes[hover_volume_idx];
|
||||
|
||||
if (hover_volume->text_configuration.has_value()) {
|
||||
m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id});
|
||||
if (type != GLGizmosManager::EType::Emboss)
|
||||
m_gizmos.open_gizmo(GLGizmosManager::EType::Emboss);
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
return;
|
||||
} else if (hover_volume->emboss_shape.has_value()) {
|
||||
m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id});
|
||||
if (type != GLGizmosManager::EType::Svg)
|
||||
m_gizmos.open_gizmo(GLGizmosManager::EType::Svg);
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_moving)
|
||||
show_sinking_contours();
|
||||
|
||||
|
@ -4460,6 +4507,9 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
|||
int instance_idx = v->instance_idx();
|
||||
int volume_idx = v->volume_idx();
|
||||
|
||||
if (volume_idx < 0)
|
||||
continue;
|
||||
|
||||
std::pair<int, int> done_id(object_idx, instance_idx);
|
||||
|
||||
if (0 <= object_idx && object_idx < (int)m_model->objects.size()) {
|
||||
|
@ -4469,10 +4519,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
|||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance)
|
||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
|
||||
else if (selection_mode == Selection::Volume) {
|
||||
if (model_object->volumes[volume_idx]->get_offset() != v->get_volume_offset()) {
|
||||
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
||||
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
|
||||
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
|
||||
// BBS: backup
|
||||
Slic3r::save_object_mesh(*model_object);
|
||||
}
|
||||
|
@ -4564,26 +4614,26 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
|||
Selection::EMode selection_mode = m_selection.get_mode();
|
||||
|
||||
for (const GLVolume* v : m_volumes.volumes) {
|
||||
int object_idx = v->object_idx();
|
||||
const int object_idx = v->object_idx();
|
||||
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
|
||||
continue;
|
||||
|
||||
int instance_idx = v->instance_idx();
|
||||
int volume_idx = v->volume_idx();
|
||||
const int instance_idx = v->instance_idx();
|
||||
const int volume_idx = v->volume_idx();
|
||||
|
||||
if (volume_idx < 0)
|
||||
continue;
|
||||
|
||||
done.insert(std::pair<int, int>(object_idx, instance_idx));
|
||||
|
||||
// Rotate instances/volumes.
|
||||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance) {
|
||||
model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation());
|
||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||
}
|
||||
if (selection_mode == Selection::Instance)
|
||||
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
|
||||
else if (selection_mode == Selection::Volume) {
|
||||
if (model_object->volumes[volume_idx]->get_rotation() != v->get_volume_rotation()) {
|
||||
model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation());
|
||||
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
||||
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
|
||||
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
|
||||
// BBS: backup
|
||||
Slic3r::save_object_mesh(*model_object);
|
||||
}
|
||||
|
@ -4645,27 +4695,27 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
|||
Selection::EMode selection_mode = m_selection.get_mode();
|
||||
|
||||
for (const GLVolume* v : m_volumes.volumes) {
|
||||
int object_idx = v->object_idx();
|
||||
const int object_idx = v->object_idx();
|
||||
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
|
||||
continue;
|
||||
|
||||
int instance_idx = v->instance_idx();
|
||||
int volume_idx = v->volume_idx();
|
||||
const int instance_idx = v->instance_idx();
|
||||
const int volume_idx = v->volume_idx();
|
||||
|
||||
if (volume_idx < 0)
|
||||
continue;
|
||||
|
||||
done.insert(std::pair<int, int>(object_idx, instance_idx));
|
||||
|
||||
// Rotate instances/volumes
|
||||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance) {
|
||||
model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor());
|
||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||
}
|
||||
if (selection_mode == Selection::Instance)
|
||||
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
|
||||
else if (selection_mode == Selection::Volume) {
|
||||
if (model_object->volumes[volume_idx]->get_scaling_factor() != v->get_volume_scaling_factor()) {
|
||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||
model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor());
|
||||
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
||||
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
|
||||
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
|
||||
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
|
||||
// BBS: backup
|
||||
Slic3r::save_object_mesh(*model_object);
|
||||
}
|
||||
|
@ -4756,10 +4806,10 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
|||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance)
|
||||
model_object->instances[instance_idx]->set_mirror(v->get_instance_mirror());
|
||||
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
|
||||
else if (selection_mode == Selection::Volume) {
|
||||
if (model_object->volumes[volume_idx]->get_mirror() != v->get_volume_mirror()) {
|
||||
model_object->volumes[volume_idx]->set_mirror(v->get_volume_mirror());
|
||||
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
|
||||
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
|
||||
// BBS: backup
|
||||
Slic3r::save_object_mesh(*model_object);
|
||||
}
|
||||
|
@ -5210,6 +5260,14 @@ bool GLCanvas3D::is_object_sinking(int object_idx) const
|
|||
return false;
|
||||
}
|
||||
|
||||
void GLCanvas3D::apply_retina_scale(Vec2d &screen_coordinate) const
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
double scale = static_cast<double>(m_retina_helper->get_scale_factor());
|
||||
screen_coordinate *= scale;
|
||||
#endif // ENABLE_RETINA_GL
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_shown_on_screen() const
|
||||
{
|
||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||
|
@ -7148,12 +7206,14 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
|
|||
|
||||
auto* m_notification = wxGetApp().plater()->get_notification_manager();
|
||||
m_notification->set_scale(sc);
|
||||
m_gizmos.set_overlay_scale(sc);
|
||||
#else
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
m_main_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size * scale);
|
||||
m_assemble_view_toolbar.set_icons_size(size);
|
||||
m_separator_toolbar.set_icons_size(size);
|
||||
collapse_toolbar.set_icons_size(size / 2.0);
|
||||
m_gizmos.set_overlay_icon_size(size);
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
// Update collapse toolbar
|
||||
|
@ -7210,31 +7270,6 @@ void GLCanvas3D::_render_overlays()
|
|||
_render_assemble_control();
|
||||
_render_assemble_info();
|
||||
|
||||
// main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed
|
||||
// to correctly place them
|
||||
#if ENABLE_RETINA_GL
|
||||
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(/*true*/);
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
m_main_toolbar.set_scale(scale);
|
||||
m_assemble_view_toolbar.set_scale(scale);
|
||||
m_separator_toolbar.set_scale(scale);
|
||||
wxGetApp().plater()->get_collapse_toolbar().set_scale(scale / 2.0);
|
||||
m_gizmos.set_overlay_scale(scale);
|
||||
#else
|
||||
// BBS adjust display scale
|
||||
const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(/*true*/));
|
||||
const float gizmo_size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale());
|
||||
//const float size = int(GLToolbar::Default_Icons_Size);
|
||||
//const float gizmo_size = int(GLGizmosManager::Default_Icons_Size);
|
||||
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
m_main_toolbar.set_icons_size(gizmo_size);
|
||||
m_assemble_view_toolbar.set_icons_size(gizmo_size);
|
||||
m_separator_toolbar.set_icons_size(gizmo_size);
|
||||
wxGetApp().plater()->get_collapse_toolbar().set_icons_size(size / 2.0);
|
||||
m_gizmos.set_overlay_icon_size(gizmo_size);
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
_render_separator_toolbar_right();
|
||||
_render_separator_toolbar_left();
|
||||
_render_main_toolbar();
|
||||
|
@ -8077,7 +8112,7 @@ void GLCanvas3D::_render_assemble_control() const
|
|||
const float text_size_x = std::max(imgui->calc_text_size(_L("Reset direction")).x + 2 * ImGui::GetStyle().FramePadding.x,
|
||||
std::max(imgui->calc_text_size(_L("Explosion Ratio")).x, imgui->calc_text_size(_L("Section View")).x));
|
||||
const float slider_width = 75.0f;
|
||||
const float value_size = imgui->calc_text_size("3.00").x + text_padding * 2;
|
||||
const float value_size = imgui->calc_text_size(std::string_view{"3.00"}).x + text_padding * 2;
|
||||
const float item_spacing = imgui->get_item_spacing().x;
|
||||
ImVec2 window_padding = ImGui::GetStyle().WindowPadding;
|
||||
|
||||
|
@ -9511,5 +9546,108 @@ void GLCanvas3D::GizmoHighlighter::blink()
|
|||
invalidate();
|
||||
}
|
||||
|
||||
const ModelVolume *get_model_volume(const GLVolume &v, const Model &model)
|
||||
{
|
||||
const ModelVolume * ret = nullptr;
|
||||
|
||||
if (v.object_idx() < (int)model.objects.size()) {
|
||||
const ModelObject *obj = model.objects[v.object_idx()];
|
||||
if (v.volume_idx() < (int)obj->volumes.size())
|
||||
ret = obj->volumes[v.volume_idx()];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects)
|
||||
{
|
||||
for (const ModelObject *obj : objects)
|
||||
for (ModelVolume *vol : obj->volumes)
|
||||
if (vol->id() == volume_id)
|
||||
return vol;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModelVolume *get_model_volume(const GLVolume &v, const ModelObject& object) {
|
||||
if (v.volume_idx() < 0)
|
||||
return nullptr;
|
||||
|
||||
size_t volume_idx = static_cast<size_t>(v.volume_idx());
|
||||
if (volume_idx >= object.volumes.size())
|
||||
return nullptr;
|
||||
|
||||
return object.volumes[volume_idx];
|
||||
}
|
||||
|
||||
ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects)
|
||||
{
|
||||
if (v.object_idx() < 0)
|
||||
return nullptr;
|
||||
size_t objext_idx = static_cast<size_t>(v.object_idx());
|
||||
if (objext_idx >= objects.size())
|
||||
return nullptr;
|
||||
if (objects[objext_idx] == nullptr)
|
||||
return nullptr;
|
||||
return get_model_volume(v, *objects[objext_idx]);
|
||||
}
|
||||
|
||||
GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas) {
|
||||
int hovered_id_signed = canvas.get_first_hover_volume_idx();
|
||||
if (hovered_id_signed < 0)
|
||||
return nullptr;
|
||||
|
||||
size_t hovered_id = static_cast<size_t>(hovered_id_signed);
|
||||
const GLVolumePtrs &volumes = canvas.get_volumes().volumes;
|
||||
if (hovered_id >= volumes.size())
|
||||
return nullptr;
|
||||
|
||||
return volumes[hovered_id];
|
||||
}
|
||||
|
||||
GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas) {
|
||||
const GLVolume *gl_volume = get_selected_gl_volume(canvas.get_selection());
|
||||
if (gl_volume == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
|
||||
for (GLVolume *v : gl_volumes)
|
||||
if (v->composite_id == gl_volume->composite_id)
|
||||
return v;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model) {
|
||||
return get_model_object(gl_volume, model.objects);
|
||||
}
|
||||
|
||||
ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects) {
|
||||
if (gl_volume.object_idx() < 0)
|
||||
return nullptr;
|
||||
size_t objext_idx = static_cast<size_t>(gl_volume.object_idx());
|
||||
if (objext_idx >= objects.size())
|
||||
return nullptr;
|
||||
return objects[objext_idx];
|
||||
}
|
||||
|
||||
ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model& model) {
|
||||
return get_model_instance(gl_volume, model.objects);
|
||||
}
|
||||
|
||||
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects) {
|
||||
if (gl_volume.instance_idx() < 0)
|
||||
return nullptr;
|
||||
ModelObject *object = get_model_object(gl_volume, objects);
|
||||
return get_model_instance(gl_volume, *object);
|
||||
}
|
||||
|
||||
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object) {
|
||||
if (gl_volume.instance_idx() < 0)
|
||||
return nullptr;
|
||||
size_t instance_idx = static_cast<size_t>(gl_volume.instance_idx());
|
||||
if (instance_idx >= object.instances.size())
|
||||
return nullptr;
|
||||
return object.instances[instance_idx];
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -966,6 +966,12 @@ public:
|
|||
Size get_canvas_size() const;
|
||||
Vec2d get_local_mouse_position() const;
|
||||
|
||||
// store opening position of menu
|
||||
std::optional<Vec2d> m_popup_menu_positon; // position of mouse right click
|
||||
void set_popup_menu_position(const Vec2d &position) { m_popup_menu_positon = position; }
|
||||
const std::optional<Vec2d>& get_popup_menu_position() const { return m_popup_menu_positon; }
|
||||
void clear_popup_menu_position() { m_popup_menu_positon.reset(); }
|
||||
|
||||
void set_tooltip(const std::string& tooltip);
|
||||
|
||||
// the following methods add a snapshot to the undo/redo stack, unless the given string is empty
|
||||
|
@ -1107,6 +1113,8 @@ public:
|
|||
|
||||
bool is_object_sinking(int object_idx) const;
|
||||
|
||||
void apply_retina_scale(Vec2d &screen_coordinate) const;
|
||||
|
||||
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
|
||||
|
||||
// Convert the screen space coordinate to an object space coordinate.
|
||||
|
@ -1233,6 +1241,21 @@ private:
|
|||
float get_overlay_window_width() { return 0; /*LayersEditing::get_overlay_window_width();*/ }
|
||||
};
|
||||
|
||||
const ModelVolume *get_model_volume(const GLVolume &v, const Model &model);
|
||||
ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects);
|
||||
ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects);
|
||||
ModelVolume *get_model_volume(const GLVolume &v, const ModelObject &object);
|
||||
|
||||
GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas);
|
||||
GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas);
|
||||
|
||||
ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model);
|
||||
ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects);
|
||||
|
||||
ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model &model);
|
||||
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects);
|
||||
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Pavel Mikuš @Godrak, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
@ -15,6 +19,8 @@
|
|||
#include "format.hpp"
|
||||
//BBS: add partplate related logic
|
||||
#include "PartPlate.hpp"
|
||||
#include "Gizmos/GLGizmoEmboss.hpp"
|
||||
#include "Gizmos/GLGizmoSVG.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||
|
@ -273,24 +279,28 @@ wxBitmapBundle* SettingsFactory::get_category_bitmap(const std::string& category
|
|||
//-------------------------------------
|
||||
|
||||
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
|
||||
#ifdef __WINDOWS__
|
||||
const std::vector<std::pair<std::string, std::string>> MenuFactory::ADD_VOLUME_MENU_ITEMS = {
|
||||
static const constexpr std::array<std::pair<const char *, const char *>, 5> ADD_VOLUME_MENU_ITEMS = {{
|
||||
// menu_item Name menu_item bitmap name
|
||||
{L("Add part"), "menu_add_part" }, // ~ModelVolumeType::MODEL_PART
|
||||
{L("Add negative part"), "menu_add_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME
|
||||
{L("Add modifier"), "menu_add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER
|
||||
{L("Add support blocker"), "menu_support_blocker"}, // ~ModelVolumeType::SUPPORT_BLOCKER
|
||||
{L("Add support enforcer"), "menu_support_enforcer"} // ~ModelVolumeType::SUPPORT_ENFORCER
|
||||
};
|
||||
#else
|
||||
const std::vector<std::pair<std::string, std::string>> MenuFactory::ADD_VOLUME_MENU_ITEMS = {
|
||||
{L("Add part"), "menu_add_part" }, // ~ModelVolumeType::MODEL_PART
|
||||
{L("Add negative part"), "menu_add_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME
|
||||
{L("Add modifier"), "menu_add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER
|
||||
{L("Add support blocker"), "menu_support_blocker"}, // ~ModelVolumeType::SUPPORT_BLOCKER
|
||||
{L("Add support enforcer"), "menu_support_enforcer"} // ~ModelVolumeType::SUPPORT_ENFORCER
|
||||
};
|
||||
{L("Add support enforcer"), "menu_support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER
|
||||
}};
|
||||
|
||||
#endif
|
||||
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
|
||||
static const constexpr std::array<std::pair<const char *, const char *>, 3> TEXT_VOLUME_ICONS {{
|
||||
// menu_item Name menu_item bitmap name
|
||||
{L("Add text"), "add_text_part"}, // ~ModelVolumeType::MODEL_PART
|
||||
{L("Add negative text"), "add_text_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME
|
||||
{L("Add text modifier"), "add_text_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER
|
||||
}};
|
||||
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
|
||||
static const constexpr std::array<std::pair<const char *, const char *>, 3> SVG_VOLUME_ICONS{{
|
||||
{L("Add SVG part"), "svg_part"}, // ~ModelVolumeType::MODEL_PART
|
||||
{L("Add negative SVG"), "svg_negative"}, // ~ModelVolumeType::NEGATIVE_VOLUME
|
||||
{L("Add SVG modifier"), "svg_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER
|
||||
}};
|
||||
|
||||
static Plater* plater()
|
||||
{
|
||||
|
@ -428,12 +438,26 @@ std::vector<wxBitmapBundle*> MenuFactory::get_volume_bitmaps()
|
|||
{
|
||||
std::vector<wxBitmapBundle*> volume_bmps;
|
||||
volume_bmps.reserve(ADD_VOLUME_MENU_ITEMS.size());
|
||||
for (auto item : ADD_VOLUME_MENU_ITEMS){
|
||||
if(!item.second.empty()){
|
||||
//volume_bmps.push_back(create_menu_bitmap(item.second));
|
||||
volume_bmps.push_back(get_bmp_bundle(item.second));
|
||||
}
|
||||
}
|
||||
for (const auto& item : ADD_VOLUME_MENU_ITEMS)
|
||||
volume_bmps.push_back(get_bmp_bundle(item.second));
|
||||
return volume_bmps;
|
||||
}
|
||||
|
||||
std::vector<wxBitmapBundle*> MenuFactory::get_text_volume_bitmaps()
|
||||
{
|
||||
std::vector<wxBitmapBundle*> volume_bmps;
|
||||
volume_bmps.reserve(TEXT_VOLUME_ICONS.size());
|
||||
for (const auto& item : TEXT_VOLUME_ICONS)
|
||||
volume_bmps.push_back(get_bmp_bundle(item.second));
|
||||
return volume_bmps;
|
||||
}
|
||||
|
||||
std::vector<wxBitmapBundle*> MenuFactory::get_svg_volume_bitmaps()
|
||||
{
|
||||
std::vector<wxBitmapBundle *> volume_bmps;
|
||||
volume_bmps.reserve(SVG_VOLUME_ICONS.size());
|
||||
for (const auto &item : SVG_VOLUME_ICONS)
|
||||
volume_bmps.push_back(get_bmp_bundle(item.second));
|
||||
return volume_bmps;
|
||||
}
|
||||
|
||||
|
@ -463,19 +487,6 @@ void MenuFactory::append_menu_item_delete(wxMenu* menu)
|
|||
#endif
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
append_menu_item(
|
||||
menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr,
|
||||
[]() { return plater()->can_edit_text(); }, m_parent);
|
||||
#else
|
||||
append_menu_item(
|
||||
menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr,
|
||||
[]() { return plater()->can_edit_text(); }, m_parent);
|
||||
#endif
|
||||
}
|
||||
|
||||
wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType type) {
|
||||
auto sub_menu = new wxMenu;
|
||||
|
||||
|
@ -509,6 +520,10 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty
|
|||
},
|
||||
"", menu);
|
||||
}
|
||||
|
||||
append_menu_item_add_text(sub_menu, type);
|
||||
append_menu_item_add_svg(sub_menu, type);
|
||||
|
||||
sub_menu->AppendSeparator();
|
||||
for (auto &item : {L("Cube"), L("Cylinder"), L("Sphere"), L("Cone")}) {
|
||||
append_menu_item(
|
||||
|
@ -522,13 +537,69 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty
|
|||
return sub_menu;
|
||||
}
|
||||
|
||||
static void append_menu_itemm_add_(const wxString& name, GLGizmosManager::EType gizmo_type, wxMenu *menu, ModelVolumeType type, bool is_submenu_item) {
|
||||
auto add_ = [type, gizmo_type](const wxCommandEvent & /*unnamed*/) {
|
||||
const GLCanvas3D *canvas = plater()->canvas3D();
|
||||
const GLGizmosManager &mng = canvas->get_gizmos_manager();
|
||||
GLGizmoBase *gizmo_base = mng.get_gizmo(gizmo_type);
|
||||
|
||||
ModelVolumeType volume_type = type;
|
||||
// no selected object means create new object
|
||||
if (volume_type == ModelVolumeType::INVALID)
|
||||
volume_type = ModelVolumeType::MODEL_PART;
|
||||
|
||||
auto screen_position = canvas->get_popup_menu_position();
|
||||
if (gizmo_type == GLGizmosManager::Emboss) {
|
||||
auto emboss = dynamic_cast<GLGizmoEmboss *>(gizmo_base);
|
||||
assert(emboss != nullptr);
|
||||
if (emboss == nullptr) return;
|
||||
if (screen_position.has_value()) {
|
||||
emboss->create_volume(volume_type, *screen_position);
|
||||
} else {
|
||||
emboss->create_volume(volume_type);
|
||||
}
|
||||
} else if (gizmo_type == GLGizmosManager::Svg) {
|
||||
auto svg = dynamic_cast<GLGizmoSVG *>(gizmo_base);
|
||||
assert(svg != nullptr);
|
||||
if (svg == nullptr) return;
|
||||
if (screen_position.has_value()) {
|
||||
svg->create_volume(volume_type, *screen_position);
|
||||
} else {
|
||||
svg->create_volume(volume_type);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (type == ModelVolumeType::MODEL_PART || type == ModelVolumeType::NEGATIVE_VOLUME || type == ModelVolumeType::PARAMETER_MODIFIER ||
|
||||
type == ModelVolumeType::INVALID // cannot use gizmo without selected object
|
||||
) {
|
||||
wxString item_name = wxString(is_submenu_item ? "" : _(ADD_VOLUME_MENU_ITEMS[int(type)].first) + ": ") + name;
|
||||
menu->AppendSeparator();
|
||||
const std::string icon_name = is_submenu_item ? "" : ADD_VOLUME_MENU_ITEMS[int(type)].second;
|
||||
append_menu_item(menu, wxID_ANY, item_name, "", add_, icon_name, menu);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, bool is_submenu_item/* = true*/){
|
||||
append_menu_itemm_add_(_L("Text"), GLGizmosManager::Emboss, menu, type, is_submenu_item);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_add_svg(wxMenu *menu, ModelVolumeType type, bool is_submenu_item /* = true*/){
|
||||
append_menu_itemm_add_(_L("SVG"), GLGizmosManager::Svg, menu, type, is_submenu_item);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
|
||||
{
|
||||
// Update "add" items(delete old & create new) settings popupmenu
|
||||
for (auto& item : ADD_VOLUME_MENU_ITEMS) {
|
||||
const auto settings_id = menu->FindItem(_(item.first));
|
||||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
const wxString item_name = _(item.first);
|
||||
int item_id = menu->FindItem(item_name);
|
||||
if (item_id != wxNOT_FOUND)
|
||||
menu->Destroy(item_id);
|
||||
|
||||
item_id = menu->FindItem(item_name + ": " + _L("Text"));
|
||||
if (item_id != wxNOT_FOUND)
|
||||
menu->Destroy(item_id);
|
||||
}
|
||||
|
||||
for (size_t type = 0; type < ADD_VOLUME_MENU_ITEMS.size(); type++)
|
||||
|
@ -992,6 +1063,81 @@ void MenuFactory::append_menu_items_mirror(wxMenu* menu)
|
|||
[]() { return plater()->can_mirror(); }, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
|
||||
{
|
||||
wxString name = _L("Edit text");
|
||||
|
||||
auto can_edit_text = []() {
|
||||
if (plater() == nullptr)
|
||||
return false;
|
||||
const Selection& selection = plater()->get_selection();
|
||||
if (selection.volumes_count() != 1)
|
||||
return false;
|
||||
const GLVolume* gl_volume = selection.get_first_volume();
|
||||
if (gl_volume == nullptr)
|
||||
return false;
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
if (volume == nullptr)
|
||||
return false;
|
||||
return volume->is_text();
|
||||
};
|
||||
|
||||
if (menu != &m_text_part_menu) {
|
||||
const int menu_item_id = menu->FindItem(name);
|
||||
if (menu_item_id != wxNOT_FOUND)
|
||||
menu->Destroy(menu_item_id);
|
||||
if (!can_edit_text())
|
||||
return;
|
||||
}
|
||||
|
||||
wxString description = _L("Ability to change text, font, size, ...");
|
||||
std::string icon = "cog";
|
||||
auto open_emboss = [](const wxCommandEvent &) {
|
||||
GLGizmosManager &mng = plater()->get_view3D_canvas3D()->get_gizmos_manager();
|
||||
if (mng.get_current_type() == GLGizmosManager::Emboss)
|
||||
mng.open_gizmo(GLGizmosManager::Emboss); // close() and reopen - move to be visible
|
||||
mng.open_gizmo(GLGizmosManager::Emboss);
|
||||
};
|
||||
append_menu_item(menu, wxID_ANY, name, description, open_emboss, icon, nullptr, can_edit_text, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_edit_svg(wxMenu *menu)
|
||||
{
|
||||
wxString name = _L("Edit SVG");
|
||||
auto can_edit_svg = []() {
|
||||
if (plater() == nullptr)
|
||||
return false;
|
||||
const Selection& selection = plater()->get_selection();
|
||||
if (selection.volumes_count() != 1)
|
||||
return false;
|
||||
const GLVolume* gl_volume = selection.get_first_volume();
|
||||
if (gl_volume == nullptr)
|
||||
return false;
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
if (volume == nullptr)
|
||||
return false;
|
||||
return volume->is_svg();
|
||||
};
|
||||
|
||||
if (menu != &m_svg_part_menu) {
|
||||
const int menu_item_id = menu->FindItem(name);
|
||||
if (menu_item_id != wxNOT_FOUND)
|
||||
menu->Destroy(menu_item_id);
|
||||
if (!can_edit_svg())
|
||||
return;
|
||||
}
|
||||
|
||||
wxString description = _L("Change SVG source file, projection, size, ...");
|
||||
std::string icon = "cog";
|
||||
auto open_svg = [](const wxCommandEvent &) {
|
||||
GLGizmosManager &mng = plater()->get_view3D_canvas3D()->get_gizmos_manager();
|
||||
if (mng.get_current_type() == GLGizmosManager::Svg)
|
||||
mng.open_gizmo(GLGizmosManager::Svg); // close() and reopen - move to be visible
|
||||
mng.open_gizmo(GLGizmosManager::Svg);
|
||||
};
|
||||
append_menu_item(menu, wxID_ANY, name, description, open_svg, icon, nullptr, can_edit_svg, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_invalidate_cut_info(wxMenu *menu)
|
||||
{
|
||||
const wxString menu_name = _L("Invalidate cut info");
|
||||
|
@ -1175,6 +1321,34 @@ void MenuFactory::create_part_menu()
|
|||
append_menu_item_per_object_settings(&m_part_menu);
|
||||
}
|
||||
|
||||
void MenuFactory::create_text_part_menu()
|
||||
{
|
||||
wxMenu* menu = &m_text_part_menu;
|
||||
|
||||
append_menu_item_edit_text(menu);
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
menu->AppendSeparator();
|
||||
append_menu_item_per_object_settings(menu);
|
||||
append_menu_item_change_type(menu);
|
||||
}
|
||||
|
||||
void MenuFactory::create_svg_part_menu()
|
||||
{
|
||||
wxMenu* menu = &m_svg_part_menu;
|
||||
|
||||
append_menu_item_edit_svg(menu);
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
menu->AppendSeparator();
|
||||
append_menu_item_per_object_settings(menu);
|
||||
append_menu_item_change_type(menu);
|
||||
}
|
||||
|
||||
void MenuFactory::create_bbl_part_menu()
|
||||
{
|
||||
wxMenu* menu = &m_part_menu;
|
||||
|
@ -1301,7 +1475,8 @@ void MenuFactory::init(wxWindow* parent)
|
|||
//create_object_menu();
|
||||
create_sla_object_menu();
|
||||
//create_part_menu();
|
||||
|
||||
create_text_part_menu();
|
||||
create_svg_part_menu();
|
||||
create_extra_object_menu();
|
||||
create_bbl_part_menu();
|
||||
create_bbl_assemble_object_menu();
|
||||
|
@ -1330,6 +1505,8 @@ wxMenu* MenuFactory::object_menu()
|
|||
append_menu_items_convert_unit(&m_object_menu);
|
||||
append_menu_items_flush_options(&m_object_menu);
|
||||
append_menu_item_invalidate_cut_info(&m_object_menu);
|
||||
append_menu_item_edit_text(&m_object_menu);
|
||||
append_menu_item_edit_svg(&m_object_menu);
|
||||
append_menu_item_change_filament(&m_object_menu);
|
||||
return &m_object_menu;
|
||||
}
|
||||
|
@ -1339,6 +1516,8 @@ wxMenu* MenuFactory::sla_object_menu()
|
|||
append_menu_items_convert_unit(&m_sla_object_menu);
|
||||
append_menu_item_settings(&m_sla_object_menu);
|
||||
//update_menu_items_instance_manipulation(mtObjectSLA);
|
||||
append_menu_item_edit_text(&m_sla_object_menu);
|
||||
append_menu_item_edit_svg(&m_object_menu);
|
||||
|
||||
return &m_sla_object_menu;
|
||||
}
|
||||
|
@ -1351,6 +1530,22 @@ wxMenu* MenuFactory::part_menu()
|
|||
return &m_part_menu;
|
||||
}
|
||||
|
||||
wxMenu* MenuFactory::text_part_menu()
|
||||
{
|
||||
append_menu_item_change_filament(&m_text_part_menu);
|
||||
append_menu_item_per_object_settings(&m_text_part_menu);
|
||||
|
||||
return &m_text_part_menu;
|
||||
}
|
||||
|
||||
wxMenu *MenuFactory::svg_part_menu()
|
||||
{
|
||||
append_menu_item_change_filament(&m_svg_part_menu);
|
||||
append_menu_item_per_object_settings(&m_svg_part_menu);
|
||||
|
||||
return &m_svg_part_menu;
|
||||
}
|
||||
|
||||
wxMenu* MenuFactory::instance_menu()
|
||||
{
|
||||
return &m_instance_menu;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2022 Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_Factories_hpp_
|
||||
#define slic3r_GUI_Factories_hpp_
|
||||
|
||||
|
@ -47,8 +51,9 @@ struct SettingsFactory
|
|||
class MenuFactory
|
||||
{
|
||||
public:
|
||||
static const std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS;
|
||||
static std::vector<wxBitmapBundle*> get_volume_bitmaps();
|
||||
static std::vector<wxBitmapBundle*> get_volume_bitmaps();
|
||||
static std::vector<wxBitmapBundle*> get_text_volume_bitmaps();
|
||||
static std::vector<wxBitmapBundle*> get_svg_volume_bitmaps();
|
||||
|
||||
MenuFactory();
|
||||
~MenuFactory() = default;
|
||||
|
@ -65,6 +70,8 @@ public:
|
|||
wxMenu* object_menu();
|
||||
wxMenu* sla_object_menu();
|
||||
wxMenu* part_menu();
|
||||
wxMenu* text_part_menu();
|
||||
wxMenu* svg_part_menu();
|
||||
wxMenu* instance_menu();
|
||||
wxMenu* layer_menu();
|
||||
wxMenu* multi_selection_menu();
|
||||
|
@ -85,6 +92,8 @@ private:
|
|||
|
||||
MenuWithSeparators m_object_menu;
|
||||
MenuWithSeparators m_part_menu;
|
||||
MenuWithSeparators m_text_part_menu;
|
||||
MenuWithSeparators m_svg_part_menu;
|
||||
MenuWithSeparators m_sla_object_menu;
|
||||
MenuWithSeparators m_default_menu;
|
||||
MenuWithSeparators m_instance_menu;
|
||||
|
@ -104,6 +113,8 @@ private:
|
|||
void create_object_menu();
|
||||
void create_sla_object_menu();
|
||||
void create_part_menu();
|
||||
void create_text_part_menu();
|
||||
void create_svg_part_menu();
|
||||
//BBS: add part plate related logic
|
||||
void create_plate_menu();
|
||||
//BBS: add bbl object menu
|
||||
|
@ -113,6 +124,8 @@ private:
|
|||
void create_bbl_assemble_part_menu();
|
||||
|
||||
wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type);
|
||||
void append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, bool is_submenu_item = true);
|
||||
void append_menu_item_add_svg(wxMenu *menu, ModelVolumeType type, bool is_submenu_item = true);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
|
@ -128,7 +141,6 @@ private:
|
|||
void append_menu_item_change_extruder(wxMenu* menu);
|
||||
void append_menu_item_set_visible(wxMenu* menu);
|
||||
void append_menu_item_delete(wxMenu* menu);
|
||||
void append_menu_item_edit_text(wxMenu *menu);
|
||||
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
|
||||
void append_menu_items_convert_unit(wxMenu* menu); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk"
|
||||
void append_menu_items_flush_options(wxMenu* menu);
|
||||
|
@ -137,6 +149,8 @@ private:
|
|||
void append_menu_item_merge_parts_to_single_part(wxMenu *menu);
|
||||
void append_menu_items_mirror(wxMenu *menu);
|
||||
void append_menu_item_invalidate_cut_info(wxMenu *menu);
|
||||
void append_menu_item_edit_text(wxMenu *menu);
|
||||
void append_menu_item_edit_svg(wxMenu *menu);
|
||||
|
||||
//void append_menu_items_instance_manipulation(wxMenu *menu);
|
||||
//void update_menu_items_instance_manipulation(MenuType type);
|
||||
|
|
|
@ -1331,11 +1331,20 @@ void ObjectList::show_context_menu(const bool evt_context_menu)
|
|||
const ItemType type = m_objects_model->GetItemType(item);
|
||||
if (!(type & (itPlate | itObject | itVolume | itInstance)))
|
||||
return;
|
||||
if (type & itVolume) {
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
if (obj_idx < 0 || vol_idx < 0)
|
||||
return;
|
||||
const ModelVolume *volume = object(obj_idx)->volumes[vol_idx];
|
||||
|
||||
menu = type & itPlate ? plater->plate_menu() :
|
||||
type & itInstance ? plater->instance_menu() :
|
||||
type & itVolume ? plater->part_menu() :
|
||||
printer_technology() == ptFFF ? plater->object_menu() : plater->sla_object_menu();
|
||||
menu = volume->is_text() ? plater->text_part_menu() :
|
||||
plater->part_menu();
|
||||
}
|
||||
else
|
||||
menu = type & itPlate ? plater->plate_menu() :
|
||||
type & itInstance ? plater->instance_menu() :
|
||||
printer_technology() == ptFFF ? plater->object_menu() : plater->sla_object_menu();
|
||||
plater->SetPlateIndexByRightMenuInLeftUI(-1);
|
||||
if (type & itPlate) {
|
||||
int plate_idx = -1;
|
||||
|
@ -1999,7 +2008,7 @@ void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& mo
|
|||
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
|
||||
const GLVolume* v = selection.get_first_volume();
|
||||
const Geometry::Transformation inst_transform = v->get_instance_transformation();
|
||||
const Transform3d inv_inst_transform = inst_transform.get_matrix(true).inverse();
|
||||
const Transform3d inv_inst_transform = inst_transform.get_matrix_no_offset().inverse();
|
||||
const Vec3d instance_offset = v->get_instance_offset();
|
||||
|
||||
for (size_t i = 0; i < input_files.size(); ++i) {
|
||||
|
@ -2143,7 +2152,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
Vec3d(0., 0., 0.5 * mesh_bb.size().z() + instance_bb.min.z() - v->get_instance_offset().z()) :
|
||||
// Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
|
||||
Vec3d(instance_bb.max.x(), instance_bb.min.y(), instance_bb.min.z()) + 0.5 * mesh_bb.size() - v->get_instance_offset();
|
||||
new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset);
|
||||
new_volume->set_offset(v->get_instance_transformation().get_matrix_no_offset().inverse() * offset);
|
||||
|
||||
// BBS: backup
|
||||
Slic3r::save_object_mesh(model_object);
|
||||
|
@ -2273,56 +2282,6 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
|
|||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
int ObjectList::load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp)
|
||||
{
|
||||
wxDataViewItem item = GetSelection();
|
||||
// we can add volumes for Object or Instance
|
||||
if (!item || !(m_objects_model->GetItemType(item) & (itObject | itInstance)))
|
||||
return -1;
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
|
||||
if (obj_idx < 0)
|
||||
return -1;
|
||||
|
||||
// Get object item, if Instance is selected
|
||||
if (m_objects_model->GetItemType(item) & itInstance)
|
||||
item = m_objects_model->GetItemById(obj_idx);
|
||||
|
||||
ModelObject* mo = (*m_objects)[obj_idx];
|
||||
|
||||
Geometry::Transformation instance_transformation = mo->instances[0]->get_transformation();
|
||||
|
||||
// apply the instance transform to all volumes and reset instance transform except the offset
|
||||
apply_object_instance_transfrom_to_all_volumes(mo, !is_temp);
|
||||
|
||||
ModelVolume *mv = mo->add_volume(mesh);
|
||||
mv->name = name.ToStdString();
|
||||
if (!text_info.m_text.empty())
|
||||
mv->set_text_info(text_info);
|
||||
|
||||
if (!is_temp) {
|
||||
std::vector<ModelVolume *> volumes;
|
||||
volumes.push_back(mv);
|
||||
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume *volume) {
|
||||
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end();
|
||||
});
|
||||
|
||||
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_object((size_t) obj_idx);
|
||||
|
||||
if (items.size() > 1) {
|
||||
m_selection_mode = smVolume;
|
||||
m_last_selected_item = wxDataViewItem(nullptr);
|
||||
}
|
||||
select_items(items);
|
||||
|
||||
selection_changed();
|
||||
}
|
||||
|
||||
//BBS: notify partplate the modify
|
||||
notify_instance_updated(obj_idx);
|
||||
return mo->volumes.size() - 1;
|
||||
}
|
||||
|
||||
//BBS
|
||||
bool ObjectList::del_object(const int obj_idx, bool refresh_immediately)
|
||||
{
|
||||
|
@ -2614,7 +2573,9 @@ void ObjectList::split()
|
|||
for (const ModelVolume* volume : model_object->volumes) {
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
|
||||
volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->is_text(),
|
||||
volume->is_svg(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
// add settings to the part, if it has those
|
||||
|
@ -3787,6 +3748,8 @@ wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, st
|
|||
object_item,
|
||||
from_u8(volume->name),
|
||||
volume->type(),
|
||||
volume->is_text(),
|
||||
volume->is_svg(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
|
@ -5838,6 +5801,8 @@ wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(int obj_idx, s
|
|||
for (const ModelVolume* volume : object->volumes) {
|
||||
wxDataViewItem vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name),
|
||||
volume->type(),
|
||||
volume->is_text(),
|
||||
volume->is_svg(),
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
|
|
|
@ -27,7 +27,6 @@ class ModelConfig;
|
|||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class TriangleMesh;
|
||||
struct TextInfo;
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
// FIXME: broken build on mac os because of this is missing:
|
||||
|
@ -287,7 +286,6 @@ public:
|
|||
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
|
||||
// BBS
|
||||
void switch_to_object_process();
|
||||
int load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp);
|
||||
bool del_object(const int obj_idx, bool refresh_immediately = true);
|
||||
void del_subobject_item(wxDataViewItem& item);
|
||||
void del_settings_from_config(const wxDataViewItem& parent_item);
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, David Kocík @kocikdav, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral
|
||||
///|/ Copyright (c) 2022 André Althaus
|
||||
///|/ Copyright (c) 2019 John Drake @foxox
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
//#include "stdlib.h"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
|
@ -65,6 +71,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig
|
|||
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
||||
// XXX: If have OpenGL
|
||||
m_canvas->enable_picking(true);
|
||||
m_canvas->get_selection().set_mode(Selection::Instance);
|
||||
m_canvas->enable_moving(true);
|
||||
// XXX: more config from 3D.pm
|
||||
m_canvas->set_model(model);
|
||||
|
|
|
@ -252,12 +252,17 @@ void GLGizmoBase::GizmoImguiEnd()
|
|||
|
||||
void GLGizmoBase::GizmoImguiSetNextWIndowPos(float &x, float y, int flag, float pivot_x, float pivot_y)
|
||||
{
|
||||
if (abs(last_input_window_width) > 0.01f) {
|
||||
if (x + last_input_window_width > m_parent.get_canvas_size().get_width()) {
|
||||
if (last_input_window_width > m_parent.get_canvas_size().get_width()) {
|
||||
GizmoImguiSetNextWIndowPos(x, y, last_input_window_width, 0, flag, pivot_x, pivot_y);
|
||||
}
|
||||
|
||||
void GLGizmoBase::GizmoImguiSetNextWIndowPos(float &x, float y, float w, float h, int flag, float pivot_x, float pivot_y)
|
||||
{
|
||||
if (abs(w) > 0.01f) {
|
||||
if (x + w > m_parent.get_canvas_size().get_width()) {
|
||||
if (w > m_parent.get_canvas_size().get_width()) {
|
||||
x = 0;
|
||||
} else {
|
||||
x = m_parent.get_canvas_size().get_width() - last_input_window_width;
|
||||
x = m_parent.get_canvas_size().get_width() - w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,6 +250,7 @@ protected:
|
|||
bool GizmoImguiBegin(const std::string& name, int flags);
|
||||
void GizmoImguiEnd();
|
||||
void GizmoImguiSetNextWIndowPos(float &x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
|
||||
void GizmoImguiSetNextWIndowPos(float &x, float y, float w, float h, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
|
||||
|
||||
void register_grabbers_for_picking();
|
||||
void unregister_grabbers_for_picking();
|
||||
|
|
|
@ -1769,7 +1769,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center,
|
|||
// respect just to the solid parts for FFF and ignore pad and supports for SLA
|
||||
if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) {
|
||||
|
||||
const auto instance_matrix = volume->get_instance_transformation().get_matrix(true);
|
||||
const auto instance_matrix = volume->get_instance_transformation().get_matrix_no_offset();
|
||||
auto volume_trafo = instance_matrix * volume->get_volume_transformation().get_matrix();
|
||||
ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * volume_trafo));
|
||||
}
|
||||
|
@ -2284,13 +2284,13 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
|
|||
render_flip_plane_button(m_connectors_editing && connectors.empty());
|
||||
|
||||
m_imgui->text(m_labels_map["Type"]);
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.00f, 0.00f, 0.00f, 1.00f));
|
||||
ImGuiWrapper::push_radio_style();
|
||||
bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug);
|
||||
type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel);
|
||||
type_changed |= render_connect_type_radio_button(CutConnectorType::Snap);
|
||||
if (type_changed)
|
||||
apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); });
|
||||
ImGui::PopStyleColor(1);
|
||||
ImGuiWrapper::pop_radio_style();
|
||||
|
||||
m_imgui->disabled_begin(m_connector_type != CutConnectorType::Plug);
|
||||
if (type_changed && m_connector_type == CutConnectorType::Dowel) {
|
||||
|
@ -2948,7 +2948,7 @@ void GLGizmoCut3D::show_tooltip_information(float x, float y)
|
|||
ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
|
||||
ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
|
||||
|
||||
caption_max += m_imgui->calc_text_size(": ").x + 35.f;
|
||||
caption_max += m_imgui->calc_text_size(std::string_view{": "}).x + 35.f;
|
||||
|
||||
float font_size = ImGui::GetFontSize();
|
||||
ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3);
|
||||
|
|
3664
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Normal file
3664
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Normal file
File diff suppressed because it is too large
Load diff
240
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
Normal file
240
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
Normal file
|
@ -0,0 +1,240 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GLGizmoEmboss_hpp_
|
||||
#define slic3r_GLGizmoEmboss_hpp_
|
||||
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "GLGizmoRotate.hpp"
|
||||
#include "slic3r/GUI/IconManager.hpp"
|
||||
#include "slic3r/GUI/SurfaceDrag.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp" // TODO: not needed
|
||||
#include "slic3r/GUI/TextLines.hpp"
|
||||
#include "slic3r/Utils/RaycastManager.hpp"
|
||||
#include "slic3r/Utils/EmbossStyleManager.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/TextConfiguration.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
class wxFont;
|
||||
namespace Slic3r{
|
||||
class AppConfig;
|
||||
class GLVolume;
|
||||
enum class ModelVolumeType : int;
|
||||
}
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
class GLGizmoEmboss : public GLGizmoBase
|
||||
{
|
||||
public:
|
||||
explicit GLGizmoEmboss(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id);
|
||||
|
||||
/// <summary>
|
||||
/// Create new embossed text volume by type on position of mouse
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
/// <param name="mouse_pos">Define position of new volume</param>
|
||||
bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
|
||||
|
||||
/// <summary>
|
||||
/// Create new text without given position
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
bool create_volume(ModelVolumeType volume_type);
|
||||
|
||||
/// <summary>
|
||||
/// Handle pressing of shortcut
|
||||
/// </summary>
|
||||
void on_shortcut_key();
|
||||
|
||||
/// <summary>
|
||||
/// Mirroring from object manipulation panel
|
||||
/// !! Emboss gizmo must be active
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis for mirroring must be one of {0,1,2}</param>
|
||||
/// <returns>True on success start job otherwise False</returns>
|
||||
bool do_mirror(size_t axis);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Call on change inside of object conatining projected volume
|
||||
/// </summary>
|
||||
/// <param name="job_cancel">Way to stop re_emboss job</param>
|
||||
/// <returns>True on success otherwise False</returns>
|
||||
static bool re_emboss(const ModelVolume &text, std::shared_ptr<std::atomic<bool>> job_cancel = nullptr);
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
std::string on_get_name() const override;
|
||||
void on_render() override;
|
||||
void on_register_raycasters_for_picking() override;
|
||||
void on_unregister_raycasters_for_picking() override;
|
||||
void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
void on_set_state() override;
|
||||
void data_changed(bool is_serializing) override; // selection changed
|
||||
void on_set_hover_id() override{ m_rotate_gizmo.set_hover_id(m_hover_id); }
|
||||
void on_enable_grabber(unsigned int id) override { m_rotate_gizmo.enable_grabber(); }
|
||||
void on_disable_grabber(unsigned int id) override { m_rotate_gizmo.disable_grabber(); }
|
||||
void on_start_dragging() override;
|
||||
void on_stop_dragging() override;
|
||||
void on_dragging(const UpdateData &data) override;
|
||||
void push_button_style(bool pressed);
|
||||
void pop_button_style();
|
||||
|
||||
/// <summary>
|
||||
/// Rotate by text on dragging rotate grabers
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Information about mouse</param>
|
||||
/// <returns>Propagete normaly return false.</returns>
|
||||
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||
|
||||
bool wants_enter_leave_snapshots() const override;
|
||||
std::string get_gizmo_entering_text() const override;
|
||||
std::string get_gizmo_leaving_text() const override;
|
||||
std::string get_action_snapshot_name() const override;
|
||||
|
||||
private:
|
||||
void volume_transformation_changing();
|
||||
void volume_transformation_changed();
|
||||
|
||||
static EmbossStyles create_default_styles();
|
||||
// localized default text
|
||||
bool init_create(ModelVolumeType volume_type);
|
||||
|
||||
void set_volume_by_selection();
|
||||
void reset_volume();
|
||||
|
||||
// create volume from text - main functionality
|
||||
bool process();
|
||||
void close();
|
||||
void draw_window();
|
||||
void draw_text_input();
|
||||
void draw_model_type();
|
||||
void draw_style_list();
|
||||
void draw_delete_style_button();
|
||||
void draw_style_rename_popup();
|
||||
void draw_style_rename_button();
|
||||
void draw_style_save_button(bool is_modified);
|
||||
void draw_style_save_as_popup();
|
||||
void draw_style_add_button();
|
||||
void init_font_name_texture();
|
||||
void draw_font_list_line();
|
||||
void draw_font_list();
|
||||
void draw_height(bool use_inch);
|
||||
void draw_depth(bool use_inch);
|
||||
|
||||
// call after set m_style_manager.get_style().prop.size_in_mm
|
||||
bool set_height();
|
||||
|
||||
bool draw_italic_button();
|
||||
bool draw_bold_button();
|
||||
void draw_advanced();
|
||||
|
||||
bool select_facename(const wxString& facename);
|
||||
|
||||
template<typename T> bool rev_input_mm(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, T step, T step_fast, const char *format, bool use_inch, const std::optional<float>& scale) const;
|
||||
|
||||
/// <summary>
|
||||
/// Reversible input float with option to restor default value
|
||||
/// TODO: make more general, static and move to ImGuiWrapper
|
||||
/// </summary>
|
||||
/// <returns>True when value changed otherwise FALSE.</returns>
|
||||
template<typename T> bool rev_input(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags = 0) const;
|
||||
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip) const;
|
||||
bool rev_slider(const std::string &name, std::optional<int>& value, const std::optional<int> *default_value,
|
||||
const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip) const;
|
||||
bool rev_slider(const std::string &name, std::optional<float>& value, const std::optional<float> *default_value,
|
||||
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip) const;
|
||||
bool rev_slider(const std::string &name, float &value, const float *default_value,
|
||||
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip) const;
|
||||
template<typename T, typename Draw> bool revertible(const std::string &name, T &value, const T *default_value,
|
||||
const std::string &undo_tooltip, float undo_offset, Draw draw) const;
|
||||
|
||||
// process mouse event
|
||||
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
|
||||
bool on_mouse_for_translate(const wxMouseEvent &mouse_event);
|
||||
void on_mouse_change_selection(const wxMouseEvent &mouse_event);
|
||||
|
||||
// When open text loaded from .3mf it could be written with unknown font
|
||||
bool m_is_unknown_font = false;
|
||||
void create_notification_not_valid_font(const TextConfiguration& tc);
|
||||
void create_notification_not_valid_font(const std::string& text);
|
||||
void remove_notification_not_valid_font();
|
||||
|
||||
struct GuiCfg;
|
||||
std::unique_ptr<const GuiCfg> m_gui_cfg;
|
||||
|
||||
// Is open tree with advanced options
|
||||
bool m_is_advanced_edit_style = false;
|
||||
|
||||
// Keep information about stored styles and loaded actual style to compare with
|
||||
Emboss::StyleManager m_style_manager;
|
||||
|
||||
// pImpl to hide implementation of FaceNames to .cpp file
|
||||
struct Facenames; // forward declaration
|
||||
std::unique_ptr<Facenames> m_face_names;
|
||||
|
||||
// Text to emboss
|
||||
std::string m_text; // Sequence of Unicode UTF8 symbols
|
||||
|
||||
// When true keep up vector otherwise relative rotation
|
||||
bool m_keep_up = true;
|
||||
|
||||
// current selected volume
|
||||
// NOTE: Be carefull could be uninitialized (removed from Model)
|
||||
ModelVolume *m_volume = nullptr;
|
||||
|
||||
// When work with undo redo stack there could be situation that
|
||||
// m_volume point to unexisting volume so One need also objectID
|
||||
ObjectID m_volume_id;
|
||||
|
||||
// True when m_text contain character unknown by selected font
|
||||
bool m_text_contain_unknown_glyph = false;
|
||||
|
||||
// cancel for previous update of volume to cancel finalize part
|
||||
std::shared_ptr<std::atomic<bool>> m_job_cancel = nullptr;
|
||||
|
||||
// Keep information about curvature of text line around surface
|
||||
TextLinesModel m_text_lines;
|
||||
void reinit_text_lines(unsigned count_lines=0);
|
||||
|
||||
// Rotation gizmo
|
||||
GLGizmoRotate m_rotate_gizmo;
|
||||
// Value is set only when dragging rotation to calculate actual angle
|
||||
std::optional<float> m_rotate_start_angle;
|
||||
|
||||
// Keep data about dragging only during drag&drop
|
||||
std::optional<SurfaceDrag> m_surface_drag;
|
||||
|
||||
// Keep old scene triangle data in AABB trees,
|
||||
// all the time it need actualize before use.
|
||||
RaycastManager m_raycast_manager;
|
||||
|
||||
// For text on scaled objects
|
||||
std::optional<float> m_scale_height;
|
||||
std::optional<float> m_scale_depth;
|
||||
void calculate_scale();
|
||||
|
||||
// drawing icons
|
||||
IconManager m_icon_manager;
|
||||
IconManager::VIcons m_icons;
|
||||
void init_icons();
|
||||
|
||||
// only temporary solution
|
||||
static const std::string M_ICON_FILENAME;
|
||||
};
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_GLGizmoEmboss_hpp_
|
|
@ -513,7 +513,7 @@ void GLGizmoFdmSupports::show_tooltip_information(float caption_max, float x, fl
|
|||
ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
|
||||
ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
|
||||
|
||||
caption_max += m_imgui->calc_text_size(": ").x + 15.f;
|
||||
caption_max += m_imgui->calc_text_size(std::string_view{": "}).x + 15.f;
|
||||
|
||||
float font_size = ImGui::GetFontSize();
|
||||
ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3);
|
||||
|
@ -584,7 +584,7 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
|
|||
|
||||
++mesh_id;
|
||||
|
||||
const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true);
|
||||
const Transform3d trafo_matrix = mi->get_matrix_no_offset() * mv->get_matrix_no_offset();
|
||||
Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast<float>().normalized();
|
||||
Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast<float>().normalized();
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ void GLGizmoFlatten::update_planes()
|
|||
ch = ch.convex_hull_3d();
|
||||
m_planes.clear();
|
||||
on_unregister_raycasters_for_picking();
|
||||
const Transform3d& inst_matrix = mo->instances.front()->get_matrix(true);
|
||||
const Transform3d &inst_matrix = mo->instances.front()->get_matrix_no_offset();
|
||||
|
||||
// Following constants are used for discarding too small polygons.
|
||||
const float minimal_area = 5.f; // in square mm (world coordinates)
|
||||
|
|
|
@ -2042,7 +2042,7 @@ void GLGizmoMeasure::show_tooltip_information(float caption_max, float x, float
|
|||
ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
|
||||
ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
|
||||
|
||||
caption_max += m_imgui->calc_text_size(": ").x + 35.f;
|
||||
caption_max += m_imgui->calc_text_size(std::string_view{": "}).x + 35.f;
|
||||
|
||||
float font_size = ImGui::GetFontSize();
|
||||
ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3);
|
||||
|
|
|
@ -451,7 +451,7 @@ void GLGizmoMeshBoolean::generate_new_volume(bool delete_input, const TriangleMe
|
|||
new_volume->set_material_id(old_volume->material_id());
|
||||
new_volume->set_offset(old_volume->get_transformation().get_offset());
|
||||
//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->translate(new_volume->get_transformation().get_matrix_no_offset() * 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);
|
||||
|
|
|
@ -341,7 +341,7 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x
|
|||
ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
|
||||
ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
|
||||
|
||||
caption_max += m_imgui->calc_text_size(": ").x + 15.f;
|
||||
caption_max += m_imgui->calc_text_size(std::string_view{": "}).x + 15.f;
|
||||
|
||||
float font_size = ImGui::GetFontSize();
|
||||
ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3);
|
||||
|
@ -408,7 +408,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
const float filter_btn_width = m_imgui->calc_text_size(m_desc.at("perform")).x + m_imgui->scaled(1.f);
|
||||
const float buttons_width = remove_btn_width + filter_btn_width + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
const float color_button_width = m_imgui->calc_text_size("").x + m_imgui->scaled(1.75f);
|
||||
const float color_button_width = m_imgui->calc_text_size(std::string_view{""}).x + m_imgui->scaled(1.75f);
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.f;
|
||||
|
|
|
@ -107,7 +107,15 @@ void GLGizmoMove3D::on_dragging(const UpdateData& data)
|
|||
m_displacement.z() = calc_projection(data);
|
||||
|
||||
Selection &selection = m_parent.get_selection();
|
||||
selection.translate(m_displacement);
|
||||
TransformationType trafo_type;
|
||||
trafo_type.set_relative();
|
||||
switch (wxGetApp().obj_manipul()->get_coordinates_type())
|
||||
{
|
||||
case ECoordinatesType::Instance: { trafo_type.set_instance(); break; }
|
||||
case ECoordinatesType::Local: { trafo_type.set_local(); break; }
|
||||
default: { break; }
|
||||
}
|
||||
selection.translate(m_displacement, trafo_type);
|
||||
}
|
||||
|
||||
void GLGizmoMove3D::on_render()
|
||||
|
|
|
@ -245,7 +245,7 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
|
|||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_matrix(true, true, false, true).inverse();
|
||||
const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_matrix_no_scaling_factor().inverse();
|
||||
|
||||
// BBS
|
||||
ColorRGBA render_color = this->get_cursor_hover_color();
|
||||
|
@ -536,8 +536,8 @@ std::vector<GLGizmoPainterBase::ProjectedHeightRange> GLGizmoPainterBase::get_pr
|
|||
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);
|
||||
mi->get_assemble_transformation().get_matrix_no_offset() :
|
||||
mi->get_transformation().get_matrix_no_offset();
|
||||
|
||||
for (int mesh_idx = 0; mesh_idx < part_volumes.size(); mesh_idx++) {
|
||||
if (mesh_idx == m_rr.mesh_id)
|
||||
|
@ -605,8 +605,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
const ModelObject *mo = m_c->selection_info()->model_object();
|
||||
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||
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);
|
||||
mi->get_assemble_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset() :
|
||||
mi->get_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset();
|
||||
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();
|
||||
|
@ -675,8 +675,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
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);
|
||||
mi->get_assemble_transformation().get_matrix_no_offset() :
|
||||
mi->get_transformation().get_matrix_no_offset();
|
||||
std::vector<const ModelVolume*> part_volumes;
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
|
@ -692,7 +692,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
else {
|
||||
trafo_matrices.emplace_back(instance_trafo* mv->get_matrix());
|
||||
}
|
||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true));
|
||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix_no_offset());
|
||||
part_volumes.push_back(mv);
|
||||
}
|
||||
|
||||
|
@ -824,8 +824,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
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);
|
||||
mi->get_assemble_transformation().get_matrix_no_offset() :
|
||||
mi->get_transformation().get_matrix_no_offset();
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
|
@ -840,7 +840,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
else {
|
||||
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
|
||||
}
|
||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix(true));
|
||||
trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix_no_offset());
|
||||
}
|
||||
|
||||
// Now "click" into all the prepared points and spill paint around them.
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp"
|
||||
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp"
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
@ -28,10 +28,16 @@ const float GLGizmoRotate::ScaleLongTooth = 0.1f; // in percent of radius
|
|||
const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
|
||||
const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
|
||||
|
||||
|
||||
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
|
||||
: GLGizmoBase(parent, "", -1)
|
||||
, m_axis(axis)
|
||||
, m_angle(0.0)
|
||||
, m_center(0.0, 0.0, 0.0)
|
||||
, m_radius(0.0f)
|
||||
, m_snap_coarse_in_radius(0.0f)
|
||||
, m_snap_coarse_out_radius(0.0f)
|
||||
, m_snap_fine_in_radius(0.0f)
|
||||
, m_snap_fine_out_radius(0.0f)
|
||||
, m_drag_color(DEFAULT_DRAG_COLOR)
|
||||
, m_highlight_color(DEFAULT_HIGHLIGHT_COLOR)
|
||||
{
|
||||
|
@ -94,19 +100,12 @@ bool GLGizmoRotate::on_init()
|
|||
|
||||
void GLGizmoRotate::on_start_dragging()
|
||||
{
|
||||
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
|
||||
m_center = m_custom_center == Vec3d::Zero() ? box.center() : m_custom_center;
|
||||
m_radius = Offset + box.radius();
|
||||
m_snap_coarse_in_radius = m_radius / 3.0f;
|
||||
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius;
|
||||
m_snap_fine_in_radius = m_radius;
|
||||
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth;
|
||||
init_data_from_selection(m_parent.get_selection());
|
||||
}
|
||||
|
||||
void GLGizmoRotate::on_dragging(const UpdateData &data)
|
||||
{
|
||||
const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection()));
|
||||
|
||||
const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray));
|
||||
const Vec2d orig_dir = Vec2d::UnitX();
|
||||
const Vec2d new_dir = mouse_pos.normalized();
|
||||
|
||||
|
@ -141,16 +140,8 @@ void GLGizmoRotate::on_render()
|
|||
return;
|
||||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||
|
||||
if (m_hover_id != 0 && !m_grabbers.front().dragging) {
|
||||
m_center = m_custom_center == Vec3d::Zero() ? box.center() : m_custom_center;
|
||||
m_radius = Offset + box.radius();
|
||||
m_snap_coarse_in_radius = m_radius / 3.0f;
|
||||
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius;
|
||||
m_snap_fine_in_radius = m_radius;
|
||||
m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth);
|
||||
}
|
||||
if (m_hover_id != 0 && !m_grabbers.front().dragging)
|
||||
init_data_from_selection(selection);
|
||||
|
||||
const double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset);
|
||||
m_grabbers.front().center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
|
||||
|
@ -168,15 +159,14 @@ void GLGizmoRotate::on_render()
|
|||
shader->start_using();
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Transform3d view_model_matrix = camera.get_view_matrix() * m_grabbers.front().matrix;
|
||||
|
||||
const Transform3d view_model_matrix = camera.get_view_matrix() * m_grabbers.front().matrix;
|
||||
shader->set_uniform("view_model_matrix", view_model_matrix);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
|
||||
const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON;
|
||||
m_old_radius = m_radius;
|
||||
|
||||
ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color);
|
||||
const ColorRGBA color = (m_hover_id != -1) ? m_drag_color : m_highlight_color;
|
||||
render_circle(color, radius_changed);
|
||||
if (m_hover_id != -1) {
|
||||
const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON;
|
||||
|
@ -192,7 +182,23 @@ void GLGizmoRotate::on_render()
|
|||
shader->stop_using();
|
||||
}
|
||||
|
||||
render_grabber(box);
|
||||
render_grabber(m_bounding_box);
|
||||
}
|
||||
|
||||
void GLGizmoRotate::init_data_from_selection(const Selection& selection)
|
||||
{
|
||||
const auto [box, box_trafo] = m_force_local_coordinate ?
|
||||
selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system();
|
||||
m_bounding_box = box;
|
||||
const std::pair<Vec3d, double> sphere = selection.get_bounding_sphere();
|
||||
m_center = sphere.first;
|
||||
m_radius = Offset + sphere.second;
|
||||
m_orient_matrix = box_trafo;
|
||||
m_orient_matrix.translation() = m_center;
|
||||
m_snap_coarse_in_radius = m_radius / 3.0f;
|
||||
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius;
|
||||
m_snap_fine_in_radius = m_radius;
|
||||
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth;
|
||||
}
|
||||
|
||||
//BBS: add input window for move
|
||||
|
@ -422,12 +428,12 @@ Transform3d GLGizmoRotate::local_transform(const Selection& selection) const
|
|||
{
|
||||
case X:
|
||||
{
|
||||
ret = Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitY()) * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitZ());
|
||||
ret = Geometry::rotation_transform(0.5 * PI * Vec3d::UnitY()) * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitZ());
|
||||
break;
|
||||
}
|
||||
case Y:
|
||||
{
|
||||
ret = Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitZ()) * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitY());
|
||||
ret = Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitY());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -438,13 +444,10 @@ Transform3d GLGizmoRotate::local_transform(const Selection& selection) const
|
|||
}
|
||||
}
|
||||
|
||||
if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes())
|
||||
ret = selection.get_first_volume()->get_instance_transformation().get_matrix(true, false, true, true) * ret;
|
||||
|
||||
return Geometry::assemble_transform(m_center) * ret;
|
||||
return m_orient_matrix * ret;
|
||||
}
|
||||
|
||||
Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const
|
||||
Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray) const
|
||||
{
|
||||
const double half_pi = 0.5 * double(PI);
|
||||
|
||||
|
@ -472,8 +475,7 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
|
|||
}
|
||||
}
|
||||
|
||||
if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes())
|
||||
m = m * selection.get_first_volume()->get_instance_transformation().get_matrix(true, false, true, true).inverse();
|
||||
m = m * Geometry::Transformation(m_orient_matrix).get_matrix_no_offset().inverse();
|
||||
|
||||
m.translate(-m_center);
|
||||
|
||||
|
@ -496,19 +498,32 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
|
|||
//BBS: GUI refactor: add obj manipulation
|
||||
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation)
|
||||
: GLGizmoBase(parent, icon_filename, sprite_id)
|
||||
, m_gizmos({ GLGizmoRotate(parent, GLGizmoRotate::X), GLGizmoRotate(parent, GLGizmoRotate::Y), GLGizmoRotate(parent, GLGizmoRotate::Z) })
|
||||
//BBS: GUI refactor: add obj manipulation
|
||||
, m_gizmos({
|
||||
GLGizmoRotate(parent, GLGizmoRotate::X),
|
||||
GLGizmoRotate(parent, GLGizmoRotate::Y),
|
||||
GLGizmoRotate(parent, GLGizmoRotate::Z) })
|
||||
//BBS: GUI refactor: add obj manipulation
|
||||
, m_object_manipulation(obj_manipulation)
|
||||
{
|
||||
load_rotoptimize_state();
|
||||
}
|
||||
|
||||
bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) {
|
||||
|
||||
bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event)
|
||||
{
|
||||
if (mouse_event.Dragging() && m_dragging) {
|
||||
// Apply new temporary rotations
|
||||
TransformationType transformation_type(
|
||||
TransformationType::World_Relative_Joint);
|
||||
TransformationType transformation_type;
|
||||
if (m_parent.get_selection().is_wipe_tower())
|
||||
transformation_type = TransformationType::World_Relative_Joint;
|
||||
else {
|
||||
switch (wxGetApp().obj_manipul()->get_coordinates_type())
|
||||
{
|
||||
default:
|
||||
case ECoordinatesType::World: { transformation_type = TransformationType::World_Relative_Joint; break; }
|
||||
case ECoordinatesType::Instance: { transformation_type = TransformationType::Instance_Relative_Joint; break; }
|
||||
case ECoordinatesType::Local: { transformation_type = TransformationType::Local_Relative_Joint; break; }
|
||||
}
|
||||
}
|
||||
if (mouse_event.AltDown())
|
||||
transformation_type.set_independent();
|
||||
m_parent.get_selection().rotate(get_rotation(), transformation_type);
|
||||
|
@ -669,11 +684,12 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
|
|||
ImVec2 button_sz = {btn_txt_sz.x + padding.x, btn_txt_sz.y + padding.y};
|
||||
ImGui::SetCursorPosX(padding.x + sz.x - button_sz.x);
|
||||
|
||||
if (wxGetApp().plater()->is_any_job_running())
|
||||
if (!wxGetApp().plater()->get_ui_job_worker().is_idle())
|
||||
imgui->disabled_begin(true);
|
||||
|
||||
if ( imgui->button(btn_txt) ) {
|
||||
wxGetApp().plater()->optimize_rotation();
|
||||
replace_job(wxGetApp().plater()->get_ui_job_worker(),
|
||||
std::make_unique<RotoptimizeJob>());
|
||||
}
|
||||
|
||||
imgui->disabled_end();
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
#define slic3r_GLGizmoRotate_hpp_
|
||||
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "../Jobs/RotoptimizeJob.hpp"
|
||||
//BBS: add size adjust related
|
||||
#include "GizmoObjectManipulation.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
class Selection;
|
||||
|
||||
class GLGizmoRotate : public GLGizmoBase
|
||||
{
|
||||
static const float Offset;
|
||||
|
@ -36,16 +35,14 @@ public:
|
|||
private:
|
||||
Axis m_axis;
|
||||
double m_angle{ 0.0 };
|
||||
Vec3d m_custom_center{Vec3d::Zero()};
|
||||
Vec3d m_center{ Vec3d::Zero() };
|
||||
float m_radius{ 0.0f };
|
||||
float m_snap_coarse_in_radius{ 0.0f };
|
||||
float m_snap_coarse_out_radius{ 0.0f };
|
||||
float m_snap_fine_in_radius{ 0.0f };
|
||||
float m_snap_fine_out_radius{ 0.0f };
|
||||
|
||||
ColorRGBA m_drag_color;
|
||||
ColorRGBA m_highlight_color;
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
Transform3d m_orient_matrix{ Transform3d::Identity() };
|
||||
|
||||
GLModel m_circle;
|
||||
GLModel m_scale;
|
||||
|
@ -62,6 +59,12 @@ private:
|
|||
float m_old_hover_radius{ 0.0f };
|
||||
float m_old_angle{ 0.0f };
|
||||
|
||||
// emboss need to draw rotation gizmo in local coordinate systems
|
||||
bool m_force_local_coordinate{ false };
|
||||
|
||||
ColorRGBA m_drag_color;
|
||||
ColorRGBA m_highlight_color;
|
||||
|
||||
public:
|
||||
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
|
||||
virtual ~GLGizmoRotate() = default;
|
||||
|
@ -71,7 +74,8 @@ public:
|
|||
|
||||
std::string get_tooltip() const override;
|
||||
|
||||
void set_center(const Vec3d &point) { m_custom_center = point; }
|
||||
void set_group_id(int group_id) { m_group_id = group_id; }
|
||||
void set_force_local_coordinate(bool use) { m_force_local_coordinate = use; }
|
||||
|
||||
void start_dragging();
|
||||
void stop_dragging();
|
||||
|
@ -109,7 +113,9 @@ private:
|
|||
Transform3d local_transform(const Selection& selection) const;
|
||||
|
||||
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
|
||||
Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const;
|
||||
Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray) const;
|
||||
|
||||
void init_data_from_selection(const Selection& selection);
|
||||
};
|
||||
|
||||
class GLGizmoRotate3D : public GLGizmoBase
|
||||
|
@ -138,13 +144,6 @@ public:
|
|||
return tooltip;
|
||||
}
|
||||
|
||||
void set_center(const Vec3d &point)
|
||||
{
|
||||
m_gizmos[X].set_center(point);
|
||||
m_gizmos[Y].set_center(point);
|
||||
m_gizmos[Z].set_center(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Postpone to Rotation
|
||||
/// </summary>
|
||||
|
@ -164,7 +163,14 @@ protected:
|
|||
for (int i = 0; i < 3; ++i)
|
||||
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
|
||||
}
|
||||
|
||||
void on_enable_grabber(unsigned int id) override {
|
||||
if (id < 3)
|
||||
m_gizmos[id].enable_grabber();
|
||||
}
|
||||
void on_disable_grabber(unsigned int id) override {
|
||||
if (id < 3)
|
||||
m_gizmos[id].disable_grabber();
|
||||
}
|
||||
bool on_is_activable() const override;
|
||||
void on_start_dragging() override;
|
||||
void on_stop_dragging() override;
|
||||
|
|
2239
src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
Normal file
2239
src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
Normal file
File diff suppressed because it is too large
Load diff
200
src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
Normal file
200
src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
Normal file
|
@ -0,0 +1,200 @@
|
|||
#ifndef slic3r_GLGizmoSVG_hpp_
|
||||
#define slic3r_GLGizmoSVG_hpp_
|
||||
|
||||
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
|
||||
// which overrides our localization "L" macro.
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "GLGizmoRotate.hpp"
|
||||
#include "slic3r/GUI/SurfaceDrag.hpp"
|
||||
#include "slic3r/GUI/GLTexture.hpp"
|
||||
#include "slic3r/Utils/RaycastManager.hpp"
|
||||
#include "slic3r/GUI/IconManager.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Slic3r{
|
||||
class ModelVolume;
|
||||
enum class ModelVolumeType : int;
|
||||
}
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
struct Texture{
|
||||
unsigned id{0};
|
||||
unsigned width{0};
|
||||
unsigned height{0};
|
||||
};
|
||||
|
||||
class GLGizmoSVG : public GLGizmoBase
|
||||
{
|
||||
public:
|
||||
explicit GLGizmoSVG(GLCanvas3D &parent);
|
||||
|
||||
/// <summary>
|
||||
/// Create new embossed text volume by type on position of mouse
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
/// <param name="mouse_pos">Define position of new volume</param>
|
||||
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||
bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
|
||||
|
||||
/// <summary>
|
||||
/// Create new text without given position
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||
bool create_volume(ModelVolumeType volume_type); // first open file dialog
|
||||
|
||||
/// <summary>
|
||||
/// Create volume from already selected svg file
|
||||
/// </summary>
|
||||
/// <param name="svg_file">File path</param>
|
||||
/// <param name="mouse_pos">Position on screen where to create volume</param>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||
bool create_volume(std::string_view svg_file, const Vec2d &mouse_pos, ModelVolumeType volume_type = ModelVolumeType::MODEL_PART);
|
||||
bool create_volume(std::string_view svg_file, ModelVolumeType volume_type = ModelVolumeType::MODEL_PART);
|
||||
|
||||
/// <summary>
|
||||
/// Check whether volume is object containing only emboss volume
|
||||
/// </summary>
|
||||
/// <param name="volume">Pointer to volume</param>
|
||||
/// <returns>True when object otherwise False</returns>
|
||||
static bool is_svg_object(const ModelVolume &volume);
|
||||
|
||||
/// <summary>
|
||||
/// Check whether volume has emboss data
|
||||
/// </summary>
|
||||
/// <param name="volume">Pointer to volume</param>
|
||||
/// <returns>True when constain emboss data otherwise False</returns>
|
||||
static bool is_svg(const ModelVolume &volume);
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
std::string on_get_name() const override;
|
||||
void on_render() override;
|
||||
void on_register_raycasters_for_picking() override;
|
||||
void on_unregister_raycasters_for_picking() override;
|
||||
void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
bool on_is_activable() const override { return true; }
|
||||
bool on_is_selectable() const override { return false; }
|
||||
void on_set_state() override;
|
||||
void data_changed(bool is_serializing) override; // selection changed
|
||||
void on_set_hover_id() override{ m_rotate_gizmo.set_hover_id(m_hover_id); }
|
||||
void on_enable_grabber(unsigned int id) override { m_rotate_gizmo.enable_grabber(); }
|
||||
void on_disable_grabber(unsigned int id) override { m_rotate_gizmo.disable_grabber(); }
|
||||
void on_start_dragging() override;
|
||||
void on_stop_dragging() override;
|
||||
void on_dragging(const UpdateData &data) override;
|
||||
|
||||
/// <summary>
|
||||
/// Rotate by text on dragging rotate grabers
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Information about mouse</param>
|
||||
/// <returns>Propagete normaly return false.</returns>
|
||||
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||
|
||||
bool wants_enter_leave_snapshots() const override;
|
||||
std::string get_gizmo_entering_text() const override;
|
||||
std::string get_gizmo_leaving_text() const override;
|
||||
std::string get_action_snapshot_name() const override;
|
||||
private:
|
||||
void set_volume_by_selection();
|
||||
void reset_volume();
|
||||
|
||||
// create volume from text - main functionality
|
||||
bool process();
|
||||
void close();
|
||||
void draw_window();
|
||||
void draw_preview();
|
||||
void draw_filename();
|
||||
void draw_depth();
|
||||
void draw_size();
|
||||
void draw_use_surface();
|
||||
void draw_distance();
|
||||
void draw_rotation();
|
||||
void draw_mirroring();
|
||||
void draw_face_the_camera();
|
||||
void draw_model_type();
|
||||
|
||||
// process mouse event
|
||||
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
|
||||
bool on_mouse_for_translate(const wxMouseEvent &mouse_event);
|
||||
|
||||
void volume_transformation_changed();
|
||||
|
||||
struct GuiCfg;
|
||||
std::unique_ptr<const GuiCfg> m_gui_cfg;
|
||||
|
||||
// actual selected only one volume - with emboss data
|
||||
ModelVolume *m_volume = nullptr;
|
||||
|
||||
// Is used to edit eboss and send changes to job
|
||||
// Inside volume is current state of shape WRT Volume
|
||||
EmbossShape m_volume_shape; // copy from m_volume for edit
|
||||
|
||||
// same index as volumes in
|
||||
std::vector<std::string> m_shape_warnings;
|
||||
|
||||
// When work with undo redo stack there could be situation that
|
||||
// m_volume point to unexisting volume so One need also objectID
|
||||
ObjectID m_volume_id;
|
||||
|
||||
// cancel for previous update of volume to cancel finalize part
|
||||
std::shared_ptr<std::atomic<bool>> m_job_cancel = nullptr;
|
||||
|
||||
// Rotation gizmo
|
||||
GLGizmoRotate m_rotate_gizmo;
|
||||
std::optional<float> m_angle;
|
||||
std::optional<float> m_distance;
|
||||
|
||||
// Value is set only when dragging rotation to calculate actual angle
|
||||
std::optional<float> m_rotate_start_angle;
|
||||
|
||||
// TODO: it should be accessible by other gizmo too.
|
||||
// May be move to plater?
|
||||
RaycastManager m_raycast_manager;
|
||||
|
||||
// When true keep up vector otherwise relative rotation
|
||||
bool m_keep_up = true;
|
||||
|
||||
// Keep size aspect ratio when True.
|
||||
bool m_keep_ratio = true;
|
||||
|
||||
// Keep data about dragging only during drag&drop
|
||||
std::optional<SurfaceDrag> m_surface_drag;
|
||||
|
||||
// For volume on scaled objects
|
||||
std::optional<float> m_scale_width;
|
||||
std::optional<float> m_scale_height;
|
||||
std::optional<float> m_scale_depth;
|
||||
void calculate_scale();
|
||||
float get_scale_for_tolerance();
|
||||
|
||||
// keep SVG data rendered on GPU
|
||||
Texture m_texture;
|
||||
|
||||
// bounding box of shape
|
||||
// Note: Scaled mm to int value by m_volume_shape.scale
|
||||
BoundingBox m_shape_bb;
|
||||
|
||||
std::string m_filename_preview;
|
||||
|
||||
IconManager m_icon_manager;
|
||||
IconManager::VIcons m_icons;
|
||||
|
||||
// only temporary solution
|
||||
static const std::string M_ICON_FILENAME;
|
||||
};
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_GLGizmoSVG_hpp_
|
|
@ -48,7 +48,7 @@ std::string GLGizmoScale3D::get_tooltip() const
|
|||
const Selection& selection = m_parent.get_selection();
|
||||
|
||||
bool single_instance = selection.is_single_full_instance();
|
||||
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
|
||||
bool single_volume = selection.is_single_volume_or_modifier();
|
||||
|
||||
Vec3f scale = 100.0f * Vec3f::Ones();
|
||||
if (single_instance)
|
||||
|
@ -85,13 +85,21 @@ bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event)
|
|||
if (mouse_event.Dragging()) {
|
||||
if (m_dragging) {
|
||||
// Apply new temporary scale factors
|
||||
TransformationType transformation_type(TransformationType::Local_Absolute_Joint);
|
||||
Selection& selection = m_parent.get_selection();
|
||||
TransformationType transformation_type;
|
||||
if (selection.is_single_full_instance()) {
|
||||
transformation_type.set_instance();
|
||||
} else if (selection.is_single_volume_or_modifier()) {
|
||||
transformation_type.set_local();
|
||||
}
|
||||
|
||||
transformation_type.set_relative();
|
||||
|
||||
if (mouse_event.AltDown())
|
||||
transformation_type.set_independent();
|
||||
|
||||
Selection& selection = m_parent.get_selection();
|
||||
selection.scale(m_scale, transformation_type);
|
||||
if (mouse_event.CmdDown()) selection.translate(m_offset, true);
|
||||
if (mouse_event.CmdDown()) selection.translate(m_offset, transformation_type);
|
||||
}
|
||||
}
|
||||
return use_grabbers(mouse_event);
|
||||
|
@ -100,24 +108,11 @@ bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event)
|
|||
void GLGizmoScale3D::data_changed(bool is_serializing) {
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
bool enable_scale_xyz = selection.is_single_full_instance() ||
|
||||
selection.is_single_volume() ||
|
||||
selection.is_single_modifier();
|
||||
selection.is_single_volume_or_modifier();
|
||||
for (unsigned int i = 0; i < 6; ++i)
|
||||
m_grabbers[i].enabled = enable_scale_xyz;
|
||||
|
||||
if (enable_scale_xyz) {
|
||||
// all volumes in the selection belongs to the same instance, any of
|
||||
// them contains the needed data, so we take the first
|
||||
const GLVolume *volume = selection.get_first_volume();
|
||||
if (selection.is_single_full_instance()) {
|
||||
set_scale(volume->get_instance_scaling_factor());
|
||||
} else if (selection.is_single_volume() ||
|
||||
selection.is_single_modifier()) {
|
||||
set_scale(volume->get_volume_scaling_factor());
|
||||
}
|
||||
} else {
|
||||
set_scale(Vec3d::Ones());
|
||||
}
|
||||
set_scale(Vec3d::Ones());
|
||||
}
|
||||
|
||||
bool GLGizmoScale3D::on_init()
|
||||
|
@ -193,7 +188,7 @@ void GLGizmoScale3D::on_render()
|
|||
const Selection& selection = m_parent.get_selection();
|
||||
|
||||
bool single_instance = selection.is_single_full_instance();
|
||||
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
|
||||
bool single_volume = selection.is_single_volume_or_modifier();
|
||||
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
|
|
@ -151,7 +151,7 @@ void GLGizmoSeam::show_tooltip_information(float caption_max, float x, float y)
|
|||
ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
|
||||
ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
|
||||
|
||||
caption_max += m_imgui->calc_text_size(": ").x + 35.f;
|
||||
caption_max += m_imgui->calc_text_size(std::string_view{": "}).x + 35.f;
|
||||
|
||||
float font_size = ImGui::GetFontSize();
|
||||
ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3);
|
||||
|
|
|
@ -582,7 +582,7 @@ void GLGizmoSimplify::on_set_state()
|
|||
|
||||
void GLGizmoSimplify::create_gui_cfg() {
|
||||
if (m_gui_cfg.has_value()) return;
|
||||
int space_size = m_imgui->calc_text_size(":MM").x;
|
||||
int space_size = m_imgui->calc_text_size(std::string_view{":MM"}).x;
|
||||
GuiCfg cfg;
|
||||
cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x,
|
||||
m_imgui->calc_text_size(tr_triangles).x)
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoText.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
|
||||
|
||||
#include "libslic3r/format.hpp"
|
||||
|
@ -152,7 +153,7 @@ void GLGizmosManager::switch_gizmos_icon_filename()
|
|||
case(EType::Seam):
|
||||
gizmo->set_icon_filename(m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg");
|
||||
break;
|
||||
case(EType::Text):
|
||||
case(EType::Emboss):
|
||||
gizmo->set_icon_filename(m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg");
|
||||
break;
|
||||
case(EType::MmuSegmentation):
|
||||
|
@ -198,7 +199,8 @@ bool GLGizmosManager::init()
|
|||
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));
|
||||
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss));
|
||||
m_gizmos.emplace_back(new GLGizmoSVG(m_parent));
|
||||
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmuSegmentation));
|
||||
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure));
|
||||
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
|
||||
|
@ -251,27 +253,6 @@ bool GLGizmosManager::init_icon_textures()
|
|||
else
|
||||
return false;
|
||||
|
||||
|
||||
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/text_B.svg", 20, 20, texture_id))
|
||||
icon_list.insert(std::make_pair((int)IC_TEXT_B, texture_id));
|
||||
else
|
||||
return false;
|
||||
|
||||
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/text_B_dark.svg", 20, 20, texture_id))
|
||||
icon_list.insert(std::make_pair((int)IC_TEXT_B_DARK, texture_id));
|
||||
else
|
||||
return false;
|
||||
|
||||
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/text_T.svg", 20, 20, texture_id))
|
||||
icon_list.insert(std::make_pair((int)IC_TEXT_T, texture_id));
|
||||
else
|
||||
return false;
|
||||
|
||||
if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/text_T_dark.svg", 20, 20, texture_id))
|
||||
icon_list.insert(std::make_pair((int)IC_TEXT_T_DARK, texture_id));
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -328,7 +309,8 @@ void GLGizmosManager::reset_all_states()
|
|||
open_gizmo(current);
|
||||
|
||||
activate_gizmo(Undefined);
|
||||
m_hover = Undefined;
|
||||
// Orca: do not clear hover state, as Emboss gizmo can be used without selection
|
||||
//m_hover = Undefined;
|
||||
}
|
||||
|
||||
bool GLGizmosManager::open_gizmo(EType type)
|
||||
|
@ -447,8 +429,6 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
|||
return dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == MmuSegmentation)
|
||||
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Text)
|
||||
return dynamic_cast<GLGizmoText*>(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Measure)
|
||||
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Cut)
|
||||
|
@ -609,7 +589,12 @@ bool GLGizmosManager::gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event) {
|
|||
// mouse is above toolbar
|
||||
if (mouse_event.LeftDown() || mouse_event.LeftDClick()) {
|
||||
mc.left = true;
|
||||
open_gizmo(gizmo);
|
||||
if (gizmo == Emboss) {
|
||||
GLGizmoBase *gizmo_emboss = m_gizmos[Emboss].get();
|
||||
dynamic_cast<GLGizmoEmboss *>(gizmo_emboss)->on_shortcut_key();
|
||||
} else {
|
||||
open_gizmo(gizmo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (mouse_event.RightDown()) {
|
||||
|
@ -1129,12 +1114,14 @@ void GLGizmosManager::do_render_overlay() const
|
|||
const float v_bottom = v_top + dv - v_offset;
|
||||
|
||||
GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + icons_size_x, top_y - icons_size_y, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
|
||||
if (idx == m_current) {
|
||||
if (idx == m_current
|
||||
// Orca: Show Svg dialog at the same place as emboss gizmo
|
||||
|| (m_current == Svg && idx == Emboss)) {
|
||||
//BBS: GUI refactor: GLToolbar&&Gizmo adjust
|
||||
//render_input_window uses a different coordination(imgui)
|
||||
//1. no need to scale by camera zoom, set {0,0} at left-up corner for imgui
|
||||
//gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top);
|
||||
gizmo->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h);
|
||||
m_gizmos[m_current]->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h);
|
||||
|
||||
is_render_current = true;
|
||||
}
|
||||
|
@ -1337,7 +1324,7 @@ std::string get_name_from_gizmo_etype(GLGizmosManager::EType type)
|
|||
return "FdmSupports";
|
||||
case GLGizmosManager::EType::Seam:
|
||||
return "Seam";
|
||||
case GLGizmosManager::EType::Text:
|
||||
case GLGizmosManager::EType::Emboss:
|
||||
return "Text";
|
||||
case GLGizmosManager::EType::MmuSegmentation:
|
||||
return "Color Painting";
|
||||
|
|
|
@ -85,8 +85,8 @@ public:
|
|||
MeshBoolean,
|
||||
FdmSupports,
|
||||
Seam,
|
||||
// BBS
|
||||
Text,
|
||||
Emboss,
|
||||
Svg,
|
||||
MmuSegmentation,
|
||||
Measure,
|
||||
Simplify,
|
||||
|
@ -163,10 +163,6 @@ public:
|
|||
IC_TOOLBAR_RESET_HOVER,
|
||||
IC_TOOLBAR_TOOLTIP,
|
||||
IC_TOOLBAR_TOOLTIP_HOVER,
|
||||
IC_TEXT_B,
|
||||
IC_TEXT_B_DARK,
|
||||
IC_TEXT_T,
|
||||
IC_TEXT_T_DARK,
|
||||
IC_NAME_COUNT,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
|
@ -19,7 +23,7 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#define MAX_NUM 9999.99
|
||||
#define MAX_SIZE "9999.99"
|
||||
#define MAX_SIZE std::string_view{"9999.99"}
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
@ -81,7 +85,7 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection)
|
|||
m_new_rotate_label_string = L("Rotation");
|
||||
m_new_scale_label_string = L("Scale ratios");
|
||||
|
||||
m_world_coordinates = true;
|
||||
m_coordinates_type = ECoordinatesType::World;
|
||||
|
||||
ObjectList* obj_list = wxGetApp().obj_list();
|
||||
if (selection.is_single_full_instance()) {
|
||||
|
@ -89,7 +93,7 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection)
|
|||
const GLVolume* volume = selection.get_first_volume();
|
||||
m_new_position = volume->get_instance_offset();
|
||||
|
||||
if (m_world_coordinates) {
|
||||
if (is_world_coordinates()) {
|
||||
m_new_rotate_label_string = L("Rotate");
|
||||
m_new_rotation = volume->get_instance_rotation() * (180. / M_PI);
|
||||
m_new_size = selection.get_scaled_instance_bounding_box().size();
|
||||
|
@ -260,7 +264,15 @@ void GizmoObjectManipulation::change_position_value(int axis, double value)
|
|||
|
||||
Selection& selection = m_glcanvas.get_selection();
|
||||
selection.setup_cache();
|
||||
selection.translate(position - m_cache.position, selection.requires_local_axes());
|
||||
TransformationType trafo_type;
|
||||
trafo_type.set_relative();
|
||||
switch (m_coordinates_type)
|
||||
{
|
||||
case ECoordinatesType::Instance: { trafo_type.set_instance(); break; }
|
||||
case ECoordinatesType::Local: { trafo_type.set_local(); break; }
|
||||
default: { break; }
|
||||
}
|
||||
selection.translate(position - m_cache.position, trafo_type);
|
||||
m_glcanvas.do_move(L("Set Position"));
|
||||
|
||||
m_cache.position = position;
|
||||
|
@ -278,14 +290,16 @@ void GizmoObjectManipulation::change_rotation_value(int axis, double value)
|
|||
|
||||
Selection& selection = m_glcanvas.get_selection();
|
||||
|
||||
TransformationType transformation_type(TransformationType::World_Relative_Joint);
|
||||
if (selection.is_single_full_instance() || selection.requires_local_axes())
|
||||
transformation_type.set_independent();
|
||||
if (selection.is_single_full_instance() && ! m_world_coordinates) {
|
||||
//FIXME Selection::rotate() does not process absoulte rotations correctly: It does not recognize the axis index, which was changed.
|
||||
// transformation_type.set_absolute();
|
||||
transformation_type.set_local();
|
||||
}
|
||||
TransformationType transformation_type;
|
||||
transformation_type.set_relative();
|
||||
if (selection.is_single_full_instance())
|
||||
transformation_type.set_independent();
|
||||
|
||||
if (is_local_coordinates())
|
||||
transformation_type.set_local();
|
||||
|
||||
if (is_instance_coordinates())
|
||||
transformation_type.set_instance();
|
||||
|
||||
selection.setup_cache();
|
||||
selection.rotate(
|
||||
|
@ -336,7 +350,7 @@ void GizmoObjectManipulation::change_size_value(int axis, double value)
|
|||
ref_size = Vec3d(instance_scale[0] * ref_size[0], instance_scale[1] * ref_size[1], instance_scale[2] * ref_size[2]);
|
||||
}
|
||||
else if (selection.is_single_full_instance())
|
||||
ref_size = m_world_coordinates ?
|
||||
ref_size = is_world_coordinates() ?
|
||||
selection.get_unscaled_instance_bounding_box().size() :
|
||||
wxGetApp().model().objects[selection.get_first_volume()->object_idx()]->raw_mesh_bounding_box().size();
|
||||
|
||||
|
@ -355,7 +369,7 @@ void GizmoObjectManipulation::do_scale(int axis, const Vec3d &scale) const
|
|||
TransformationType transformation_type(TransformationType::World_Relative_Joint);
|
||||
if (selection.is_single_full_instance()) {
|
||||
transformation_type.set_absolute();
|
||||
if (! m_world_coordinates)
|
||||
if (! is_world_coordinates())
|
||||
transformation_type.set_local();
|
||||
}
|
||||
|
||||
|
@ -364,7 +378,7 @@ void GizmoObjectManipulation::do_scale(int axis, const Vec3d &scale) const
|
|||
scaling_factor = scale(axis) * Vec3d::Ones();
|
||||
|
||||
selection.setup_cache();
|
||||
selection.scale(scaling_factor * 0.01, transformation_type);
|
||||
selection.scale_legacy(scaling_factor * 0.01, transformation_type);
|
||||
m_glcanvas.do_scale(L("Set Scale"));
|
||||
}
|
||||
|
||||
|
@ -386,6 +400,51 @@ void GizmoObjectManipulation::on_change(const std::string& opt_key, int axis, do
|
|||
change_size_value(axis, new_value);
|
||||
}
|
||||
|
||||
void GizmoObjectManipulation::set_uniform_scaling(const bool new_value)
|
||||
{
|
||||
const Selection &selection = m_glcanvas.get_selection();
|
||||
if (selection.is_single_full_instance() && is_world_coordinates() && !new_value) {
|
||||
// Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible.
|
||||
// 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_first_volume();
|
||||
// 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
|
||||
|
||||
// Bake the rotation into the meshes of the object.
|
||||
wxGetApp().model().objects[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id);
|
||||
// Update the 3D scene, selections etc.
|
||||
wxGetApp().plater()->update();
|
||||
// Recalculate cached values at this panel, refresh the screen.
|
||||
this->UpdateAndShow(true);
|
||||
}
|
||||
}
|
||||
m_uniform_scale = new_value;
|
||||
}
|
||||
|
||||
void GizmoObjectManipulation::set_coordinates_type(ECoordinatesType type)
|
||||
{
|
||||
if (wxGetApp().get_mode() == comSimple)
|
||||
type = ECoordinatesType::World;
|
||||
|
||||
if (m_coordinates_type == type)
|
||||
return;
|
||||
|
||||
m_coordinates_type = type;
|
||||
this->UpdateAndShow(true);
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
canvas->get_gizmos_manager().update_data();
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
|
||||
ECoordinatesType GizmoObjectManipulation::get_coordinates_type() const
|
||||
{
|
||||
return m_coordinates_type;
|
||||
}
|
||||
|
||||
void GizmoObjectManipulation::reset_position_value()
|
||||
{
|
||||
Selection& selection = m_glcanvas.get_selection();
|
||||
|
@ -427,7 +486,7 @@ void GizmoObjectManipulation::reset_rotation_value()
|
|||
return;
|
||||
|
||||
// Update rotation at the GLVolumes.
|
||||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
m_glcanvas.do_rotate(L("Reset Rotation"));
|
||||
|
@ -444,30 +503,6 @@ void GizmoObjectManipulation::reset_scale_value()
|
|||
change_scale_value(2, 100.);
|
||||
}
|
||||
|
||||
void GizmoObjectManipulation::set_uniform_scaling(const bool new_value)
|
||||
{
|
||||
const Selection &selection = m_glcanvas.get_selection();
|
||||
if (selection.is_single_full_instance() && m_world_coordinates && !new_value) {
|
||||
// Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible.
|
||||
// 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_first_volume();
|
||||
// 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
|
||||
|
||||
// Bake the rotation into the meshes of the object.
|
||||
wxGetApp().model().objects[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id);
|
||||
// Update the 3D scene, selections etc.
|
||||
wxGetApp().plater()->update();
|
||||
// Recalculate cached values at this panel, refresh the screen.
|
||||
this->UpdateAndShow(true);
|
||||
}
|
||||
}
|
||||
m_uniform_scale = new_value;
|
||||
}
|
||||
|
||||
static const char* label_values[2][3] = {
|
||||
{ "##position_x", "##position_y", "##position_z"},
|
||||
{ "##rotation_x", "##rotation_y", "##rotation_z"}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "libslic3r/Point.hpp"
|
||||
#include <float.h>
|
||||
|
||||
#include "slic3r/GUI/GUI_Geometry.hpp"
|
||||
|
||||
//#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -81,8 +83,7 @@ public:
|
|||
Vec3d m_buffered_size;
|
||||
bool m_new_enabled {true};
|
||||
bool m_uniform_scale {true};
|
||||
// Does the object manipulation panel work in World or Local coordinates?
|
||||
bool m_world_coordinates = true;
|
||||
ECoordinatesType m_coordinates_type{ ECoordinatesType::World };
|
||||
|
||||
bool m_show_clear_rotation { false };
|
||||
bool m_show_clear_scale { false };
|
||||
|
@ -107,9 +108,12 @@ public:
|
|||
|
||||
void set_uniform_scaling(const bool uniform_scale);
|
||||
bool get_uniform_scaling() const { return m_uniform_scale; }
|
||||
// Does the object manipulation panel work in World or Local coordinates?
|
||||
void set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; this->UpdateAndShow(true); }
|
||||
bool get_world_coordinates() const { return m_world_coordinates; }
|
||||
|
||||
void set_coordinates_type(ECoordinatesType type);
|
||||
ECoordinatesType get_coordinates_type() const;
|
||||
bool is_world_coordinates() const { return m_coordinates_type == ECoordinatesType::World; }
|
||||
bool is_instance_coordinates() const { return m_coordinates_type == ECoordinatesType::Instance; }
|
||||
bool is_local_coordinates() const { return m_coordinates_type == ECoordinatesType::Local; }
|
||||
|
||||
void reset_cache() { m_cache.reset(); }
|
||||
|
||||
|
|
410
src/slic3r/GUI/IconManager.cpp
Normal file
410
src/slic3r/GUI/IconManager.cpp
Normal file
|
@ -0,0 +1,410 @@
|
|||
#include "IconManager.hpp"
|
||||
#include <cmath>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
#include "libslic3r/Utils.hpp" // ScopeGuard
|
||||
|
||||
#include "3DScene.hpp" // glsafe
|
||||
#include "GL/glew.h"
|
||||
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#include "imgui/imstb_rectpack.h" // distribute rectangles
|
||||
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
namespace priv {
|
||||
// set shared pointer to point on bad texture
|
||||
static void clear(IconManager::Icons &icons);
|
||||
static const std::vector<std::pair<int, bool>>& get_states(IconManager::RasterType type);
|
||||
static void draw_transparent_icon(const IconManager::Icon &icon); // only help function
|
||||
}
|
||||
|
||||
IconManager::~IconManager() {
|
||||
priv::clear(m_icons);
|
||||
// release opengl texture is made in ~GLTexture()
|
||||
|
||||
if (m_id != 0)
|
||||
glsafe(::glDeleteTextures(1, &m_id));
|
||||
}
|
||||
|
||||
namespace {
|
||||
NSVGimage *parse_file(const char * filepath) {
|
||||
FILE *fp = boost::nowide::fopen(filepath, "rb");
|
||||
assert(fp != nullptr);
|
||||
if (fp == nullptr)
|
||||
return nullptr;
|
||||
|
||||
Slic3r::ScopeGuard sg([fp]() { fclose(fp); });
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
// Note: +1 is for null termination
|
||||
auto data_ptr = std::make_unique<char[]>(size+1);
|
||||
data_ptr[size] = '\0'; // Must be null terminated.
|
||||
|
||||
size_t readed_size = fread(data_ptr.get(), 1, size, fp);
|
||||
assert(readed_size == size);
|
||||
if (readed_size != size)
|
||||
return nullptr;
|
||||
|
||||
return nsvgParse(data_ptr.get(), "px", 96.0f);
|
||||
}
|
||||
|
||||
void subdata(unsigned char *data, size_t data_stride, const std::vector<unsigned char> &data2, size_t data2_row) {
|
||||
assert(data_stride >= data2_row);
|
||||
for (size_t data2_offset = 0, data_offset = 0;
|
||||
data2_offset < data2.size();
|
||||
data2_offset += data2_row, data_offset += data_stride)
|
||||
::memcpy((void *)(data + data_offset), (const void *)(data2.data() + data2_offset), data2_row);
|
||||
}
|
||||
}
|
||||
|
||||
IconManager::Icons IconManager::init(const InitTypes &input)
|
||||
{
|
||||
assert(!input.empty());
|
||||
if (input.empty())
|
||||
return {};
|
||||
|
||||
// TODO: remove in future
|
||||
if (m_id != 0) {
|
||||
glsafe(::glDeleteTextures(1, &m_id));
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
int total_surface = 0;
|
||||
for (const InitType &i : input)
|
||||
total_surface += i.size.x * i.size.y;
|
||||
const int surface_sqrt = (int)sqrt((float)total_surface) + 1;
|
||||
|
||||
// Start packing
|
||||
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
|
||||
const int TEX_HEIGHT_MAX = 1024 * 32;
|
||||
int width = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
|
||||
|
||||
int num_nodes = width;
|
||||
std::vector<stbrp_node> nodes(num_nodes);
|
||||
stbrp_context context;
|
||||
stbrp_init_target(&context, width, TEX_HEIGHT_MAX, nodes.data(), num_nodes);
|
||||
|
||||
ImVector<stbrp_rect> pack_rects;
|
||||
pack_rects.resize(input.size());
|
||||
memset(pack_rects.Data, 0, (size_t) pack_rects.size_in_bytes());
|
||||
for (size_t i = 0; i < input.size(); i++) {
|
||||
const ImVec2 &size = input[i].size;
|
||||
assert(size.x > 1);
|
||||
assert(size.y > 1);
|
||||
pack_rects[i].w = size.x;
|
||||
pack_rects[i].h = size.y;
|
||||
}
|
||||
int pack_rects_res = stbrp_pack_rects(&context, &pack_rects[0], pack_rects.Size);
|
||||
assert(pack_rects_res == 1);
|
||||
if (pack_rects_res != 1)
|
||||
return {};
|
||||
|
||||
ImVec2 tex_size(width, width);
|
||||
for (const stbrp_rect &rect : pack_rects) {
|
||||
float x = rect.x + rect.w;
|
||||
float y = rect.y + rect.h;
|
||||
if(x > tex_size.x) tex_size.x = x;
|
||||
if(y > tex_size.y) tex_size.y = y;
|
||||
}
|
||||
|
||||
Icons result(input.size());
|
||||
for (int i = 0; i < pack_rects.Size; i++) {
|
||||
const stbrp_rect &rect = pack_rects[i];
|
||||
assert(rect.was_packed);
|
||||
if (!rect.was_packed)
|
||||
return {};
|
||||
|
||||
ImVec2 tl(rect.x / tex_size.x, rect.y / tex_size.y);
|
||||
ImVec2 br((rect.x + rect.w) / tex_size.x, (rect.y + rect.h) / tex_size.y);
|
||||
|
||||
assert(input[i].size.x == rect.w);
|
||||
assert(input[i].size.y == rect.h);
|
||||
Icon icon = {input[i].size, tl, br};
|
||||
result[i] = std::make_shared<Icon>(std::move(icon));
|
||||
}
|
||||
|
||||
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||
assert(rast != nullptr);
|
||||
if (rast == nullptr)
|
||||
return {};
|
||||
ScopeGuard sg_rast([rast]() { ::nsvgDeleteRasterizer(rast); });
|
||||
|
||||
int channels = 4;
|
||||
int n_pixels = tex_size.x * tex_size.y;
|
||||
// store data for whole texture
|
||||
std::vector<unsigned char> data(n_pixels * channels, {0});
|
||||
|
||||
// initialize original index locations
|
||||
std::vector<size_t> idx(input.size());
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
|
||||
// Group same filename by sort inputs
|
||||
// sort indexes based on comparing values in input
|
||||
std::sort(idx.begin(), idx.end(), [&input](size_t i1, size_t i2) { return input[i1].filepath < input[i2].filepath; });
|
||||
for (size_t j: idx) {
|
||||
const InitType &i = input[j];
|
||||
if (i.filepath.empty())
|
||||
continue; // no file path only reservation of space for texture
|
||||
assert(boost::filesystem::exists(i.filepath));
|
||||
if (!boost::filesystem::exists(i.filepath))
|
||||
continue;
|
||||
assert(boost::algorithm::iends_with(i.filepath, ".svg"));
|
||||
if (!boost::algorithm::iends_with(i.filepath, ".svg"))
|
||||
continue;
|
||||
|
||||
NSVGimage *image = parse_file(i.filepath.c_str());
|
||||
assert(image != nullptr);
|
||||
if (image == nullptr)
|
||||
return {};
|
||||
|
||||
ScopeGuard sg_image([image]() { ::nsvgDelete(image); });
|
||||
|
||||
float svg_scale = i.size.y / image->height;
|
||||
// scale should be same in both directions
|
||||
assert(is_approx(svg_scale, i.size.y / image->width));
|
||||
|
||||
const stbrp_rect &rect = pack_rects[j];
|
||||
int n_pixels = rect.w * rect.h;
|
||||
std::vector<unsigned char> icon_data(n_pixels * channels, {0});
|
||||
::nsvgRasterize(rast, image, 0, 0, svg_scale, icon_data.data(), i.size.x, i.size.y, i.size.x * channels);
|
||||
|
||||
// makes white or gray only data in icon
|
||||
if (i.type == RasterType::white_only_data ||
|
||||
i.type == RasterType::gray_only_data) {
|
||||
unsigned char value = (i.type == RasterType::white_only_data) ? 255 : 127;
|
||||
for (size_t k = 0; k < icon_data.size(); k += channels)
|
||||
if (icon_data[k] != 0 || icon_data[k + 1] != 0 || icon_data[k + 2] != 0) {
|
||||
icon_data[k] = value;
|
||||
icon_data[k + 1] = value;
|
||||
icon_data[k + 2] = value;
|
||||
}
|
||||
}
|
||||
|
||||
int start_offset = (rect.y*tex_size.x + rect.x) * channels;
|
||||
int data_stride = tex_size.x * channels;
|
||||
subdata(data.data() + start_offset, data_stride, icon_data, rect.w * channels);
|
||||
}
|
||||
|
||||
if (m_id != 0)
|
||||
glsafe(::glDeleteTextures(1, &m_id));
|
||||
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint) m_id));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) tex_size.x, (GLsizei) tex_size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*) data.data()));
|
||||
|
||||
// bind no texture
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
for (const auto &i : result)
|
||||
i->tex_id = m_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<IconManager::Icons> IconManager::init(const std::vector<std::string> &file_paths, const ImVec2 &size, RasterType type)
|
||||
{
|
||||
assert(!file_paths.empty());
|
||||
assert(size.x >= 1);
|
||||
assert(size.x < 256*16);
|
||||
|
||||
// TODO: remove in future
|
||||
if (!m_icons.empty()) {
|
||||
// not first initialization
|
||||
priv::clear(m_icons);
|
||||
m_icons.clear();
|
||||
m_icons_texture.reset();
|
||||
}
|
||||
|
||||
// only rectangle are supported
|
||||
assert(size.x == size.y);
|
||||
// no subpixel supported
|
||||
unsigned int width = static_cast<unsigned int>(std::abs(std::round(size.x)));
|
||||
assert(size.x == static_cast<float>(width));
|
||||
|
||||
// state order has to match the enum IconState
|
||||
const auto& states = priv::get_states(type);
|
||||
|
||||
bool compress = false;
|
||||
bool is_loaded = m_icons_texture.load_from_svg_files_as_sprites_array(file_paths, states, width, compress);
|
||||
if (!is_loaded || (size_t) m_icons_texture.get_width() < (states.size() * width) ||
|
||||
(size_t) m_icons_texture.get_height() < (file_paths.size() * width)) {
|
||||
// bad load of icons, but all usage of m_icons_texture check that texture is initialized
|
||||
assert(false);
|
||||
m_icons_texture.reset();
|
||||
return {};
|
||||
}
|
||||
|
||||
unsigned count_files = file_paths.size();
|
||||
// count icons per file
|
||||
unsigned count = states.size();
|
||||
// create result
|
||||
std::vector<Icons> result;
|
||||
result.reserve(count_files);
|
||||
|
||||
Icon def_icon;
|
||||
def_icon.tex_id = m_icons_texture.get_id();
|
||||
def_icon.size = size;
|
||||
|
||||
// float beacouse of dividing
|
||||
float tex_height = static_cast<float>(m_icons_texture.get_height());
|
||||
float tex_width = static_cast<float>(m_icons_texture.get_width());
|
||||
|
||||
//for (const auto &f: file_paths) {
|
||||
for (unsigned f = 0; f < count_files; ++f) {
|
||||
// NOTE: there are space between icons
|
||||
unsigned start_y = static_cast<unsigned>(f) * (width + 1) + 1;
|
||||
float y1 = start_y / tex_height;
|
||||
float y2 = (start_y + width) / tex_height;
|
||||
Icons file_icons;
|
||||
file_icons.reserve(count);
|
||||
//for (const auto &s : states) {
|
||||
for (unsigned j = 0; j < count; ++j) {
|
||||
auto icon = std::make_shared<Icon>(def_icon);
|
||||
// NOTE: there are space between icons
|
||||
unsigned start_x = static_cast<unsigned>(j) * (width + 1) + 1;
|
||||
float x1 = start_x / tex_width;
|
||||
float x2 = (start_x + width) / tex_width;
|
||||
icon->tl = ImVec2(x1, y1);
|
||||
icon->br = ImVec2(x2, y2);
|
||||
file_icons.push_back(icon);
|
||||
m_icons.push_back(std::move(icon));
|
||||
}
|
||||
result.emplace_back(std::move(file_icons));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void IconManager::release() {
|
||||
BOOST_LOG_TRIVIAL(error) << "Not implemented yet";
|
||||
}
|
||||
|
||||
void priv::clear(IconManager::Icons &icons) {
|
||||
std::string message;
|
||||
for (auto &icon : icons) {
|
||||
// Exist more than this instance of shared ptr?
|
||||
long count = icon.use_count();
|
||||
if (count != 1) {
|
||||
// in existing icon change texture to non existing one
|
||||
icon->tex_id = 0;
|
||||
|
||||
std::string descr =
|
||||
((count > 2) ? (std::to_string(count - 1) + "x") : "") + // count
|
||||
std::to_string(icon->size.x) + "x" + std::to_string(icon->size.y); // resolution
|
||||
if (message.empty())
|
||||
message = descr;
|
||||
else
|
||||
message += ", " + descr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.empty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "There is still used icons(" << message << ").";
|
||||
}
|
||||
|
||||
const std::vector<std::pair<int, bool>> &priv::get_states(IconManager::RasterType type) {
|
||||
static std::vector<std::pair<int, bool>> color = {std::make_pair(0, false)};
|
||||
static std::vector<std::pair<int, bool>> white = {std::make_pair(1, false)};
|
||||
static std::vector<std::pair<int, bool>> gray = {std::make_pair(2, false)};
|
||||
static std::vector<std::pair<int, bool>> color_wite_gray = {
|
||||
std::make_pair(1, false), // Activable
|
||||
std::make_pair(0, false), // Hovered
|
||||
std::make_pair(2, false) // Disabled
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case IconManager::RasterType::color: return color;
|
||||
case IconManager::RasterType::white_only_data: return white;
|
||||
case IconManager::RasterType::gray_only_data: return gray;
|
||||
case IconManager::RasterType::color_wite_gray: return color_wite_gray;
|
||||
default: return color;
|
||||
}
|
||||
}
|
||||
|
||||
void priv::draw_transparent_icon(const IconManager::Icon &icon)
|
||||
{
|
||||
// Check input
|
||||
if (!icon.is_valid()) {
|
||||
assert(false);
|
||||
BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon.";
|
||||
ImGui::Text("?");
|
||||
return;
|
||||
}
|
||||
|
||||
// size UV texture coors [in texture ratio]
|
||||
ImVec2 size_uv(icon.br.x - icon.tl.x, icon.br.y - icon.tl.y);
|
||||
ImVec2 one_px(size_uv.x / icon.size.x, size_uv.y / icon.size.y);
|
||||
|
||||
// use top left corner of first icon
|
||||
IconManager::Icon icon_px = icon; // copy
|
||||
// reduce uv coors to one pixel
|
||||
icon_px.tl = ImVec2(0, 0);
|
||||
icon_px.br = one_px;
|
||||
draw(icon_px);
|
||||
}
|
||||
|
||||
#include "imgui/imgui_internal.h" //ImGuiWindow
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col)
|
||||
{
|
||||
// Check input
|
||||
if (!icon.is_valid()) {
|
||||
assert(false);
|
||||
BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon.";
|
||||
ImGui::Text("?");
|
||||
return;
|
||||
}
|
||||
ImTextureID id = (void *)static_cast<intptr_t>(icon.tex_id);
|
||||
const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size;
|
||||
|
||||
// Orca: Align icon center vertically
|
||||
ImGuiWindow *window = ImGui::GetCurrentWindow();
|
||||
ImGuiContext &g = *GImGui;
|
||||
float cursor_y = window->DC.CursorPos.y;
|
||||
float line_height = ImGui::GetTextLineHeight() + g.Style.FramePadding.y * 2;
|
||||
float offset_y = (line_height - s.y) / 2;
|
||||
window->DC.CursorPos.y += offset_y;
|
||||
|
||||
ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col);
|
||||
|
||||
// Reset offset
|
||||
window->DC.CursorPosPrevLine.y = cursor_y;
|
||||
}
|
||||
|
||||
bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover)
|
||||
{
|
||||
// check of hover
|
||||
ImGuiWindow *window = ImGui::GetCurrentWindow();
|
||||
float cursor_x = ImGui::GetCursorPosX()
|
||||
- window->DC.GroupOffset.x
|
||||
- window->DC.ColumnsOffset.x;
|
||||
priv::draw_transparent_icon(icon);
|
||||
ImGui::SameLine(cursor_x);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
// redraw image
|
||||
draw(icon_hover);
|
||||
} else {
|
||||
// redraw normal image
|
||||
draw(icon);
|
||||
}
|
||||
return ImGui::IsItemClicked();
|
||||
}
|
||||
|
||||
bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled)
|
||||
{
|
||||
if (disabled) {
|
||||
draw(disable);
|
||||
return false;
|
||||
}
|
||||
return clickable(activ, hover);
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
134
src/slic3r/GUI/IconManager.hpp
Normal file
134
src/slic3r/GUI/IconManager.hpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#ifndef slic3r_IconManager_hpp_
|
||||
#define slic3r_IconManager_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "imgui/imgui.h" // ImVec2
|
||||
#include "slic3r/GUI/GLTexture.hpp" // texture storage
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
/// <summary>
|
||||
/// Keep texture with icons for UI
|
||||
/// Manage texture live -> create and destruct texture
|
||||
/// by live of icon shared pointers.
|
||||
/// </summary>
|
||||
class IconManager
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Release texture
|
||||
/// Set shared pointers to invalid texture
|
||||
/// </summary>
|
||||
~IconManager();
|
||||
|
||||
/// <summary>
|
||||
/// Define way to convert svg data to raster
|
||||
/// </summary>
|
||||
enum class RasterType: int{
|
||||
color = 1 << 1,
|
||||
white_only_data = 1 << 2,
|
||||
gray_only_data = 1 << 3,
|
||||
color_wite_gray = color | white_only_data | gray_only_data
|
||||
// TODO: add type with backgrounds
|
||||
};
|
||||
|
||||
struct InitType {
|
||||
// path to file with image .. svg
|
||||
std::string filepath;
|
||||
|
||||
// resolution of stored rasterized icon
|
||||
ImVec2 size; // float will be rounded
|
||||
|
||||
// could contain more than one type
|
||||
RasterType type = RasterType::color;
|
||||
// together color, white and gray = color | white_only_data | gray_only_data
|
||||
};
|
||||
using InitTypes = std::vector<InitType>;
|
||||
|
||||
/// <summary>
|
||||
/// Data for render texture with icon
|
||||
/// </summary>
|
||||
struct Icon {
|
||||
// stored texture size
|
||||
ImVec2 size = ImVec2(-1, -1); // [in px] --> unsigned int values stored as float
|
||||
|
||||
// SubTexture UV coordinate in range from 0. to 1.
|
||||
ImVec2 tl; // top left -> uv0
|
||||
ImVec2 br; // bottom right -> uv1
|
||||
|
||||
// OpenGL texture id
|
||||
unsigned int tex_id = 0;
|
||||
bool is_valid() const { return tex_id != 0;}
|
||||
// && size.x > 0 && size.y > 0 && tl.x != br.x && tl.y != br.y;
|
||||
};
|
||||
using Icons = std::vector<std::shared_ptr<Icon> >;
|
||||
// Vector of icons, each vector contain multiple use of a SVG render
|
||||
using VIcons = std::vector<Icons>;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize raster texture on GPU with given images
|
||||
/// NOTE: Have to be called after OpenGL initialization
|
||||
/// </summary>
|
||||
/// <param name="input">Define files and its size with rasterization</param>
|
||||
/// <returns>Rasterized icons stored on GPU,
|
||||
/// Same size and order as input, each item of vector is set of texture in order by RasterType</returns>
|
||||
Icons init(const InitTypes &input);
|
||||
|
||||
/// <summary>
|
||||
/// Initialize multiple icons with same settings for size and type
|
||||
/// NOTE: Have to be called after OpenGL initialization
|
||||
/// </summary>
|
||||
/// <param name="file_paths">Define files with icon</param>
|
||||
/// <param name="size">Size of stored texture[in px], float will be rounded</param>
|
||||
/// <param name="type">Define way to rasterize icon,
|
||||
/// together color, white and gray = RasterType::color | RasterType::white_only_data | RasterType::gray_only_data</param>
|
||||
/// <returns>Rasterized icons stored on GPU,
|
||||
/// Same size and order as file_paths, each item of vector is set of texture in order by RasterType</returns>
|
||||
VIcons init(const std::vector<std::string> &file_paths, const ImVec2 &size, RasterType type = RasterType::color);
|
||||
|
||||
/// <summary>
|
||||
/// Release icons which are hold only by this manager
|
||||
/// May change texture and position of icons.
|
||||
/// </summary>
|
||||
void release();
|
||||
|
||||
private:
|
||||
// keep data stored on GPU
|
||||
GLTexture m_icons_texture;
|
||||
|
||||
unsigned int m_id{ 0 };
|
||||
Icons m_icons;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Draw imgui image with icon
|
||||
/// </summary>
|
||||
/// <param name="icon">Place in texture</param>
|
||||
/// <param name="size">[optional]Size of image, wen zero than use same size as stored texture</param>
|
||||
/// <param name="tint_col">viz ImGui::Image </param>
|
||||
/// <param name="border_col">viz ImGui::Image </param>
|
||||
void draw(const IconManager::Icon &icon,
|
||||
const ImVec2 &size = ImVec2(0, 0),
|
||||
const ImVec4 &tint_col = ImVec4(1, 1, 1, 1),
|
||||
const ImVec4 &border_col = ImVec4(0, 0, 0, 0));
|
||||
|
||||
/// <summary>
|
||||
/// Draw icon which change on hover
|
||||
/// </summary>
|
||||
/// <param name="icon">Draw when no hover</param>
|
||||
/// <param name="icon_hover">Draw when hover</param>
|
||||
/// <returns>True when click, otherwise False</returns>
|
||||
bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover);
|
||||
|
||||
/// <summary>
|
||||
/// Use icon as button with 3 states activ hover and disabled
|
||||
/// </summary>
|
||||
/// <param name="activ">Not disabled not hovered image</param>
|
||||
/// <param name="hover">Hovered image</param>
|
||||
/// <param name="disable">Disabled image</param>
|
||||
/// <returns>True when click on enabled, otherwise False</returns>
|
||||
bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled = false);
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
#endif // slic3r_IconManager_hpp_
|
|
@ -520,9 +520,24 @@ void ImGuiWrapper::render()
|
|||
m_new_frame_open = false;
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::calc_text_size(std::string_view text,
|
||||
bool hide_text_after_double_hash,
|
||||
float wrap_width)
|
||||
{
|
||||
return ImGui::CalcTextSize(text.data(), text.data() + text.length(),
|
||||
hide_text_after_double_hash, wrap_width);
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::calc_text_size(const std::string& text,
|
||||
bool hide_text_after_double_hash,
|
||||
float wrap_width)
|
||||
{
|
||||
return ImGui::CalcTextSize(text.c_str(), NULL, hide_text_after_double_hash, wrap_width);
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text,
|
||||
bool hide_text_after_double_hash,
|
||||
float wrap_width) const
|
||||
float wrap_width)
|
||||
{
|
||||
auto text_utf8 = into_u8(text);
|
||||
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, hide_text_after_double_hash, wrap_width);
|
||||
|
@ -585,8 +600,8 @@ bool ImGuiWrapper::bbl_combo_with_filter(const char* label, const std::string& p
|
|||
|
||||
static char pattern_buffer[256] = { 0 };
|
||||
auto simple_match = [](const char* pattern, const char* str) {
|
||||
wxString sub_str = wxString(pattern).Lower();
|
||||
wxString main_str = wxString(str).Lower();
|
||||
wxString sub_str = wxString::FromUTF8(pattern).Lower();
|
||||
wxString main_str = wxString::FromUTF8(str).Lower();
|
||||
return main_str.Find(sub_str);
|
||||
};
|
||||
|
||||
|
@ -911,13 +926,13 @@ void ImGuiWrapper::text(const char *label)
|
|||
|
||||
void ImGuiWrapper::text(const std::string &label)
|
||||
{
|
||||
this->text(label.c_str());
|
||||
ImGuiWrapper::text(label.c_str());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::text(const wxString &label)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
this->text(label_utf8.c_str());
|
||||
ImGuiWrapper::text(label_utf8.c_str());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
|
||||
|
@ -927,13 +942,13 @@ void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
|
|||
|
||||
void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label)
|
||||
{
|
||||
this->text_colored(color, label.c_str());
|
||||
ImGuiWrapper::text_colored(color, label.c_str());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
this->text_colored(color, label_utf8.c_str());
|
||||
ImGuiWrapper::text_colored(color, label_utf8.c_str());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::text_wrapped(const char *label, float wrap_width)
|
||||
|
@ -1940,6 +1955,364 @@ ColorRGBA ImGuiWrapper::from_ImVec4(const ImVec4& color)
|
|||
return { color.x, color.y, color.z, color.w };
|
||||
}
|
||||
|
||||
template <typename T, typename Func>
|
||||
static bool input_optional(std::optional<T> &v, Func& f, std::function<bool(const T&)> is_default, const T& def_val)
|
||||
{
|
||||
if (v.has_value()) {
|
||||
if (f(*v)) {
|
||||
if (is_default(*v)) v.reset();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
T val = def_val;
|
||||
if (f(val)) {
|
||||
if (!is_default(val)) v = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_optional_int(const char * label,
|
||||
std::optional<int>& v,
|
||||
int step,
|
||||
int step_fast,
|
||||
ImGuiInputTextFlags flags,
|
||||
int def_val)
|
||||
{
|
||||
auto func = [&](int &value) {
|
||||
return ImGui::InputInt(label, &value, step, step_fast, flags);
|
||||
};
|
||||
std::function<bool(const int &)> is_default =
|
||||
[def_val](const int &value) -> bool { return value == def_val; };
|
||||
return input_optional(v, func, is_default, def_val);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_optional_float(const char * label,
|
||||
std::optional<float> &v,
|
||||
float step,
|
||||
float step_fast,
|
||||
const char * format,
|
||||
ImGuiInputTextFlags flags,
|
||||
float def_val)
|
||||
{
|
||||
auto func = [&](float &value) {
|
||||
return ImGui::InputFloat(label, &value, step, step_fast, format, flags);
|
||||
};
|
||||
std::function<bool(const float &)> is_default =
|
||||
[def_val](const float &value) -> bool {
|
||||
return std::fabs(value-def_val) <= std::numeric_limits<float>::epsilon();
|
||||
};
|
||||
return input_optional(v, func, is_default, def_val);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::drag_optional_float(const char * label,
|
||||
std::optional<float> &v,
|
||||
float v_speed,
|
||||
float v_min,
|
||||
float v_max,
|
||||
const char * format,
|
||||
float power,
|
||||
float def_val)
|
||||
{
|
||||
auto func = [&](float &value) {
|
||||
return ImGui::DragFloat(label, &value, v_speed, v_min, v_max, format, power);
|
||||
};
|
||||
std::function<bool(const float &)> is_default =
|
||||
[def_val](const float &value) -> bool {
|
||||
return std::fabs(value-def_val) <= std::numeric_limits<float>::epsilon();
|
||||
};
|
||||
return input_optional(v, func, is_default, def_val);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::slider_optional_float(const char *label,
|
||||
std::optional<float> &v,
|
||||
float v_min,
|
||||
float v_max,
|
||||
const char *format,
|
||||
float power,
|
||||
bool clamp,
|
||||
const wxString &tooltip,
|
||||
bool show_edit_btn,
|
||||
float def_val)
|
||||
{
|
||||
auto func = [&](float &value) {
|
||||
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
};
|
||||
std::function<bool(const float &)> is_default =
|
||||
[def_val](const float &value) -> bool {
|
||||
return std::fabs(value - def_val) <= std::numeric_limits<float>::epsilon();
|
||||
};
|
||||
return input_optional(v, func, is_default, def_val);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::slider_optional_int(const char *label,
|
||||
std::optional<int> &v,
|
||||
int v_min,
|
||||
int v_max,
|
||||
const char *format,
|
||||
float power,
|
||||
bool clamp,
|
||||
const wxString &tooltip,
|
||||
bool show_edit_btn,
|
||||
int def_val)
|
||||
{
|
||||
std::optional<float> val;
|
||||
if (v.has_value()) val = static_cast<float>(*v);
|
||||
auto func = [&](float &value) {
|
||||
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
};
|
||||
std::function<bool(const float &)> is_default =
|
||||
[def_val](const float &value) -> bool {
|
||||
return std::fabs(value - def_val) < 0.9f;
|
||||
};
|
||||
|
||||
float default_value = static_cast<float>(def_val);
|
||||
if (input_optional(val, func, is_default, default_value)) {
|
||||
if (val.has_value())
|
||||
v = static_cast<int>(std::round(*val));
|
||||
else
|
||||
v.reset();
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
std::optional<ImVec2> ImGuiWrapper::change_window_position(const char *window_name, bool try_to_fix) {
|
||||
ImGuiWindow *window = ImGui::FindWindowByName(window_name);
|
||||
// is window just created
|
||||
if (window == NULL)
|
||||
return {};
|
||||
|
||||
// position of window on screen
|
||||
ImVec2 position = window->Pos;
|
||||
ImVec2 size = window->SizeFull;
|
||||
|
||||
// screen size
|
||||
ImVec2 screen = ImGui::GetMainViewport()->Size;
|
||||
|
||||
std::optional<ImVec2> output_window_offset;
|
||||
if (position.x < 0) {
|
||||
if (position.y < 0)
|
||||
// top left
|
||||
output_window_offset = ImVec2(0, 0);
|
||||
else
|
||||
// only left
|
||||
output_window_offset = ImVec2(0, position.y);
|
||||
} else if (position.y < 0) {
|
||||
// only top
|
||||
output_window_offset = ImVec2(position.x, 0);
|
||||
} else if (screen.x < (position.x + size.x)) {
|
||||
if (screen.y < (position.y + size.y))
|
||||
// right bottom
|
||||
output_window_offset = ImVec2(screen.x - size.x, screen.y - size.y);
|
||||
else
|
||||
// only right
|
||||
output_window_offset = ImVec2(screen.x - size.x, position.y);
|
||||
} else if (screen.y < (position.y + size.y)) {
|
||||
// only bottom
|
||||
output_window_offset = ImVec2(position.x, screen.y - size.y);
|
||||
}
|
||||
|
||||
if (!try_to_fix && output_window_offset.has_value())
|
||||
output_window_offset = ImVec2(-1, -1); // Put on default position
|
||||
|
||||
return output_window_offset;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::left_inputs() {
|
||||
ImGui::ClearActiveID();
|
||||
}
|
||||
|
||||
std::string ImGuiWrapper::trunc(const std::string &text,
|
||||
float width,
|
||||
const char * tail)
|
||||
{
|
||||
float text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
if (text_width < width) return text;
|
||||
float tail_width = ImGui::CalcTextSize(tail).x;
|
||||
assert(width > tail_width);
|
||||
if (width <= tail_width) return "Error: Can't add tail and not be under wanted width.";
|
||||
float allowed_width = width - tail_width;
|
||||
|
||||
// guess approx count of letter
|
||||
float average_letter_width = calc_text_size(std::string_view("n")).x; // average letter width
|
||||
unsigned count_letter = static_cast<unsigned>(allowed_width / average_letter_width);
|
||||
|
||||
std::string_view text_ = text;
|
||||
std::string_view result_text = text_.substr(0, count_letter);
|
||||
text_width = calc_text_size(result_text).x;
|
||||
if (text_width < allowed_width) {
|
||||
// increase letter count
|
||||
while (count_letter < text.length()) {
|
||||
++count_letter;
|
||||
std::string_view act_text = text_.substr(0, count_letter);
|
||||
text_width = calc_text_size(act_text).x;
|
||||
if (text_width > allowed_width) break;
|
||||
result_text = act_text;
|
||||
}
|
||||
} else {
|
||||
// decrease letter count
|
||||
while (count_letter > 1) {
|
||||
--count_letter;
|
||||
result_text = text_.substr(0, count_letter);
|
||||
text_width = calc_text_size(result_text).x;
|
||||
if (text_width < allowed_width) break;
|
||||
}
|
||||
}
|
||||
return std::string(result_text) + tail;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::escape_double_hash(std::string &text)
|
||||
{
|
||||
// add space between hashes
|
||||
const std::string search = "##";
|
||||
const std::string replace = "# #";
|
||||
size_t pos = 0;
|
||||
while ((pos = text.find(search, pos)) != std::string::npos)
|
||||
text.replace(pos, search.length(), replace);
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::suggest_location(const ImVec2 &dialog_size,
|
||||
const Slic3r::Polygon &interest,
|
||||
const ImVec2 &canvas_size)
|
||||
{
|
||||
// IMPROVE 1: do not select place over menu
|
||||
// BoundingBox top_menu;
|
||||
// GLGizmosManager &gizmo_mng = canvas->get_gizmos_manager();
|
||||
// BoundingBox side_menu; // gizmo_mng.get_size();
|
||||
// BoundingBox left_bottom_menu; // is permanent?
|
||||
// NotificationManager *notify_mng = plater->get_notification_manager();
|
||||
// BoundingBox notifications; // notify_mng->get_size();
|
||||
// m_window_width, m_window_height + position
|
||||
|
||||
// IMPROVE 2: use polygon of interest not only bounding box
|
||||
BoundingBox bb(interest.points);
|
||||
Point center = bb.center(); // interest.centroid();
|
||||
|
||||
// area size
|
||||
Point window_center(canvas_size.x / 2, canvas_size.y / 2);
|
||||
|
||||
// mov on side
|
||||
Point bb_half_size = (bb.max - bb.min) / 2 + Point(1,1);
|
||||
Point diff_center = window_center - center;
|
||||
Vec2d diff_norm(diff_center.x() / (double) bb_half_size.x(),
|
||||
diff_center.y() / (double) bb_half_size.y());
|
||||
if (diff_norm.x() > 1.) diff_norm.x() = 1.;
|
||||
if (diff_norm.x() < -1.) diff_norm.x() = -1.;
|
||||
if (diff_norm.y() > 1.) diff_norm.y() = 1.;
|
||||
if (diff_norm.y() < -1.) diff_norm.y() = -1.;
|
||||
|
||||
Vec2d abs_diff(abs(diff_norm.x()), abs(diff_norm.y()));
|
||||
if (abs_diff.x() < 1. && abs_diff.y() < 1.) {
|
||||
if (abs_diff.x() > abs_diff.y())
|
||||
diff_norm.x() = (diff_norm.x() < 0.) ? (-1.) : 1.;
|
||||
else
|
||||
diff_norm.y() = (diff_norm.y() < 0.) ? (-1.) : 1.;
|
||||
}
|
||||
|
||||
Point half_dialog_size(dialog_size.x / 2., dialog_size.y / 2.);
|
||||
Point move_size = bb_half_size + half_dialog_size;
|
||||
Point offseted_center = center - half_dialog_size;
|
||||
Vec2d offset(offseted_center.x() + diff_norm.x() * move_size.x(),
|
||||
offseted_center.y() + diff_norm.y() * move_size.y());
|
||||
|
||||
// move offset close to center
|
||||
Points window_polygon = {offset.cast<int>(),
|
||||
Point(offset.x(), offset.y() + dialog_size.y),
|
||||
Point(offset.x() + dialog_size.x,
|
||||
offset.y() + dialog_size.y),
|
||||
Point(offset.x() + dialog_size.x, offset.y())};
|
||||
// check that position by Bounding box is not intersecting
|
||||
assert(Slic3r::intersection(interest, Polygon(window_polygon)).empty());
|
||||
|
||||
double allowed_space = 10; // in px
|
||||
double allowed_space_sq = allowed_space * allowed_space;
|
||||
Vec2d move_vec = (center - (offset.cast<int>() + half_dialog_size))
|
||||
.cast<double>();
|
||||
Vec2d result_move(0, 0);
|
||||
do {
|
||||
move_vec = move_vec / 2.;
|
||||
Point move_point = (move_vec + result_move).cast<int>();
|
||||
Points moved_polygon = window_polygon; // copy
|
||||
for (Point &p : moved_polygon) p += move_point;
|
||||
if (Slic3r::intersection(interest, Polygon(moved_polygon)).empty())
|
||||
result_move += move_vec;
|
||||
|
||||
} while (move_vec.squaredNorm() >= allowed_space_sq);
|
||||
offset += result_move;
|
||||
|
||||
return ImVec2(offset.x(), offset.y());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::draw(
|
||||
const Polygon &polygon,
|
||||
ImDrawList * draw_list /* = ImGui::GetOverlayDrawList()*/,
|
||||
ImU32 color /* = ImGui::GetColorU32(COL_ORANGE_LIGHT)*/,
|
||||
float thickness /* = 3.f*/)
|
||||
{
|
||||
// minimal one line consist of 2 points
|
||||
if (polygon.size() < 2) return;
|
||||
// need a place to draw
|
||||
if (draw_list == nullptr) return;
|
||||
|
||||
const Point *prev_point = &polygon.points.back();
|
||||
for (const Point &point : polygon.points) {
|
||||
ImVec2 p1(prev_point->x(), prev_point->y());
|
||||
ImVec2 p2(point.x(), point.y());
|
||||
draw_list->AddLine(p1, p2, color, thickness);
|
||||
prev_point = &point;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) {
|
||||
auto draw_list = ImGui::GetOverlayDrawList();
|
||||
draw_list->AddCircle(position, radius, color, num_segments, thickness);
|
||||
auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}};
|
||||
for (const ImVec2 &dir : dirs) {
|
||||
ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius);
|
||||
ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius);
|
||||
draw_list->AddLine(start, end, color, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::contain_all_glyphs(const ImFont *font,
|
||||
const std::string &text)
|
||||
{
|
||||
if (font == nullptr) return false;
|
||||
if (!font->IsLoaded()) return false;
|
||||
const ImFontConfig *fc = font->ConfigData;
|
||||
if (fc == nullptr) return false;
|
||||
if (text.empty()) return true;
|
||||
return is_chars_in_ranges(fc->GlyphRanges, text.c_str());
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::is_char_in_ranges(const ImWchar *ranges,
|
||||
unsigned int letter)
|
||||
{
|
||||
for (const ImWchar *range = ranges; range[0] && range[1]; range += 2) {
|
||||
ImWchar from = range[0];
|
||||
ImWchar to = range[1];
|
||||
if (from <= letter && letter <= to) return true;
|
||||
if (letter < to) return false; // ranges should be sorted
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool ImGuiWrapper::is_chars_in_ranges(const ImWchar *ranges,
|
||||
const char *chars_ptr)
|
||||
{
|
||||
while (*chars_ptr) {
|
||||
unsigned int c = 0;
|
||||
// UTF-8 to 32-bit character need imgui_internal
|
||||
int c_len = ImTextCharFromUtf8(&c, chars_ptr, NULL);
|
||||
chars_ptr += c_len;
|
||||
if (c_len == 0) break;
|
||||
if (!is_char_in_ranges(ranges, c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
static const ImWchar ranges_keyboard_shortcuts[] =
|
||||
{
|
||||
|
@ -2204,20 +2577,20 @@ void ImGuiWrapper::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.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_BorderActive, COL_ORCA);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, to_ImVec4(to_rgba(ColorRGB::ORCA(), 0.5f)));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, COL_ORCA);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, COL_ORCA);
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG_DARK);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, {1.00f, 1.00f, 1.00f, 0.0f});
|
||||
} else {
|
||||
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.68f, 0.26f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.5f));
|
||||
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_BorderActive, COL_ORCA);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, to_ImVec4(to_rgba(ColorRGB::ORCA(), 0.5f)));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, COL_ORCA);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, COL_ORCA);
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, {1.00f, 1.00f, 1.00f, 0.0f});
|
||||
}
|
||||
|
@ -2229,6 +2602,20 @@ void ImGuiWrapper::pop_combo_style()
|
|||
ImGui::PopStyleColor(7);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::push_radio_style()
|
||||
{
|
||||
if (m_is_dark_mode) {
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.00f, 0.00f, 0.00f, 1.00f));
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::pop_radio_style()
|
||||
{
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::init_font(bool compress)
|
||||
{
|
||||
destroy_font();
|
||||
|
|
|
@ -13,10 +13,11 @@
|
|||
#include <wx/string.h>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Color.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
class ColorRGBA;
|
||||
namespace Search {
|
||||
struct OptionViewParameters;
|
||||
} // namespace Search
|
||||
|
@ -95,13 +96,19 @@ public:
|
|||
|
||||
float get_font_size() const { return m_font_size; }
|
||||
float get_style_scaling() const { return m_style_scaling; }
|
||||
const ImWchar *get_glyph_ranges() const { return m_glyph_ranges; } // language specific
|
||||
|
||||
void new_frame();
|
||||
void render();
|
||||
|
||||
float scaled(float x) const { return x * m_font_size; }
|
||||
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
||||
ImVec2 calc_text_size(const wxString &text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f) const;
|
||||
/// <summary>
|
||||
/// Extend ImGui::CalcTextSize to use string_view
|
||||
/// </summary>
|
||||
static ImVec2 calc_text_size(std::string_view text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
|
||||
static ImVec2 calc_text_size(const std::string& text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
|
||||
static ImVec2 calc_text_size(const wxString &text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
|
||||
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
|
||||
|
||||
ImVec2 get_item_spacing() const;
|
||||
|
@ -137,12 +144,12 @@ public:
|
|||
bool bbl_checkbox(const wxString &label, bool &value);
|
||||
bool bbl_radio_button(const char *label, bool active);
|
||||
bool bbl_sliderin(const char *label, int *v, int v_min, int v_max, const char *format = "%d", ImGuiSliderFlags flags = 0);
|
||||
void text(const char *label);
|
||||
void text(const std::string &label);
|
||||
void text(const wxString &label);
|
||||
void text_colored(const ImVec4& color, const char* label);
|
||||
void text_colored(const ImVec4& color, const std::string& label);
|
||||
void text_colored(const ImVec4& color, const wxString& label);
|
||||
static void text(const char *label);
|
||||
static void text(const std::string &label);
|
||||
static void text(const wxString &label);
|
||||
static void text_colored(const ImVec4& color, const char* label);
|
||||
static void text_colored(const ImVec4& color, const std::string& label);
|
||||
static void text_colored(const ImVec4& color, const wxString& label);
|
||||
void text_wrapped(const char *label, float wrap_width);
|
||||
void text_wrapped(const std::string &label, float wrap_width);
|
||||
void text_wrapped(const wxString &label, float wrap_width);
|
||||
|
@ -196,11 +203,109 @@ public:
|
|||
bool want_text_input() const;
|
||||
bool want_any_input() const;
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
// Optional inputs are used for set up value inside of an optional, with default value
|
||||
//
|
||||
// Extended function ImGui::InputInt to work with std::optional<int>, when value == def_val optional is released.
|
||||
static bool input_optional_int(const char *label, std::optional<int>& v, int step=1, int step_fast=100, ImGuiInputTextFlags flags=0, int def_val = 0);
|
||||
// Extended function ImGui::InputFloat to work with std::optional<float> value near def_val cause release of optional
|
||||
static bool input_optional_float(const char* label, std::optional<float> &v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0, float def_val = .0f);
|
||||
// Extended function ImGui::DragFloat to work with std::optional<float> value near def_val cause release of optional
|
||||
static bool drag_optional_float(const char* label, std::optional<float> &v, float v_speed, float v_min, float v_max, const char* format, float power, float def_val = .0f);
|
||||
// Extended function ImGuiWrapper::slider_float to work with std::optional<float> value near def_val cause release of optional
|
||||
bool slider_optional_float(const char* label, std::optional<float> &v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, float def_val = .0f);
|
||||
// Extended function ImGuiWrapper::slider_float to work with std::optional<int>, when value == def_val than optional release its value
|
||||
bool slider_optional_int(const char* label, std::optional<int> &v, int v_min, int v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, int def_val = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Change position of imgui window
|
||||
/// </summary>
|
||||
/// <param name="window_name">ImGui identifier of window</param>
|
||||
/// <param name="output_window_offset">[output] optional </param>
|
||||
/// <param name="try_to_fix">When True Only move to be full visible otherwise reset position</param>
|
||||
/// <returns>New offset of window for function ImGui::SetNextWindowPos</returns>
|
||||
static std::optional<ImVec2> change_window_position(const char *window_name, bool try_to_fix);
|
||||
|
||||
/// <summary>
|
||||
/// Use ImGui internals to unactivate (lose focus) in input.
|
||||
/// When input is activ it can't change value by application.
|
||||
/// </summary>
|
||||
static void left_inputs();
|
||||
|
||||
/// <summary>
|
||||
/// Truncate text by ImGui draw function to specific width
|
||||
/// NOTE 1: ImGui must be initialized
|
||||
/// NOTE 2: Calculation for actual acive imgui font
|
||||
/// </summary>
|
||||
/// <param name="text">Text to be truncated</param>
|
||||
/// <param name="width">Maximal width before truncate</param>
|
||||
/// <param name="tail">String puted on end of text to be visible truncation</param>
|
||||
/// <returns>Truncated text</returns>
|
||||
static std::string trunc(const std::string &text,
|
||||
float width,
|
||||
const char *tail = " ..");
|
||||
|
||||
/// <summary>
|
||||
/// Escape ## in data by add space between hashes
|
||||
/// Needed when user written text is visualized by ImGui.
|
||||
/// </summary>
|
||||
/// <param name="text">In/Out text to be escaped</param>
|
||||
static void escape_double_hash(std::string &text);
|
||||
|
||||
/// <summary>
|
||||
/// Suggest loacation of dialog window,
|
||||
/// dependent on actual visible thing on platter
|
||||
/// like Gizmo menu size, notifications, ...
|
||||
/// To be near of polygon interest and not over it.
|
||||
/// And also not out of visible area.
|
||||
/// </summary>
|
||||
/// <param name="dialog_size">Define width and height of diaog window</param>
|
||||
/// <param name="interest">Area of interest. Result should be close to it</param>
|
||||
/// <param name="canvas_size">Available space a.k.a GLCanvas3D::get_current_canvas3D()</param>
|
||||
/// <returns>Suggestion for dialog offest</returns>
|
||||
static ImVec2 suggest_location(const ImVec2 &dialog_size,
|
||||
const Slic3r::Polygon &interest,
|
||||
const ImVec2 &canvas_size);
|
||||
|
||||
/// <summary>
|
||||
/// Visualization of polygon
|
||||
/// </summary>
|
||||
/// <param name="polygon">Define what to draw</param>
|
||||
/// <param name="draw_list">Define where to draw it</param>
|
||||
/// <param name="color">Color of polygon</param>
|
||||
/// <param name="thickness">Width of polygon line</param>
|
||||
static void draw(const Polygon &polygon,
|
||||
ImDrawList * draw_list = ImGui::GetOverlayDrawList(),
|
||||
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
|
||||
float thickness = 3.f);
|
||||
|
||||
/// <summary>
|
||||
/// Draw symbol of cross hair
|
||||
/// </summary>
|
||||
/// <param name="position">Center of cross hair</param>
|
||||
/// <param name="radius">Circle radius</param>
|
||||
/// <param name="color">Color of symbol</param>
|
||||
/// <param name="num_segments">Precission of circle</param>
|
||||
/// <param name="thickness">Thickness of Line in symbol</param>
|
||||
static void draw_cross_hair(const ImVec2 &position,
|
||||
float radius = 16.f,
|
||||
ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)),
|
||||
int num_segments = 0,
|
||||
float thickness = 4.f);
|
||||
|
||||
/// <summary>
|
||||
/// Check that font ranges contain all chars in string
|
||||
/// (rendered Unicodes are stored in GlyphRanges)
|
||||
/// </summary>
|
||||
/// <param name="font">Contain glyph ranges</param>
|
||||
/// <param name="text">Vector of character to check</param>
|
||||
/// <returns>True when all glyphs from text are in font ranges</returns>
|
||||
static bool contain_all_glyphs(const ImFont *font, const std::string &text);
|
||||
static bool is_chars_in_ranges(const ImWchar *ranges, const char *chars_ptr);
|
||||
static bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
|
||||
|
||||
bool requires_extra_frame() const { return m_requires_extra_frame; }
|
||||
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
||||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
void disable_background_fadeout_animation();
|
||||
|
||||
|
@ -248,6 +353,8 @@ public:
|
|||
static void pop_button_disable_style();
|
||||
static void push_combo_style(const float scale);
|
||||
static void pop_combo_style();
|
||||
static void push_radio_style();
|
||||
static void pop_radio_style();
|
||||
|
||||
//BBS
|
||||
static int TOOLBAR_WINDOW_FLAGS;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "ArrangeJob.hpp"
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
|
@ -509,21 +513,12 @@ void ArrangeJob::check_unprintable()
|
|||
}
|
||||
}
|
||||
|
||||
void ArrangeJob::on_exception(const std::exception_ptr &eptr)
|
||||
void ArrangeJob::process(Ctl &ctl)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (libnest2d::GeometryException &) {
|
||||
show_error(m_plater, _(L("Arrange failed. "
|
||||
"Found some exceptions when processing object geometries.")));
|
||||
} catch (std::exception &) {
|
||||
PlaterJob::on_exception(eptr);
|
||||
}
|
||||
}
|
||||
static const auto arrangestr = _u8L("Arranging");
|
||||
ctl.update_status(0, arrangestr);
|
||||
ctl.call_on_main_thread([this]{ prepare(); }).wait();;
|
||||
|
||||
void ArrangeJob::process()
|
||||
{
|
||||
auto & partplate_list = m_plater->get_partplate_list();
|
||||
|
||||
const Slic3r::DynamicPrintConfig& global_config = wxGetApp().preset_bundle->full_config();
|
||||
|
@ -543,10 +538,10 @@ void ArrangeJob::process()
|
|||
|
||||
BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << bedpts[0].transpose() << ", " << bedpts[1].transpose() << ", " << bedpts[2].transpose() << ", " << bedpts[3].transpose();
|
||||
|
||||
params.stopcondition = [this]() { return was_canceled(); };
|
||||
params.stopcondition = [&ctl]() { return ctl.was_canceled(); };
|
||||
|
||||
params.progressind = [this](unsigned num_finished, std::string str = "") {
|
||||
update_status(num_finished, _L("Arranging") + " "+ wxString::FromUTF8(str));
|
||||
params.progressind = [this, &ctl](unsigned num_finished, std::string str = "") {
|
||||
ctl.update_status(num_finished * 100 / status_range(), _u8L("Arranging") + str);
|
||||
};
|
||||
|
||||
{
|
||||
|
@ -590,11 +585,13 @@ void ArrangeJob::process()
|
|||
}
|
||||
|
||||
// finalize just here.
|
||||
update_status(status_range(),
|
||||
was_canceled() ? _(L("Arranging canceled.")) :
|
||||
we_have_unpackable_items ? _(L("Arranging is done but there are unpacked items. Reduce spacing and try again.")) : _(L("Arranging done.")));
|
||||
ctl.update_status(100,
|
||||
ctl.was_canceled() ? _u8L("Arranging canceled.") :
|
||||
we_have_unpackable_items ? _u8L("Arranging is done but there are unpacked items. Reduce spacing and try again.") : _u8L("Arranging done."));
|
||||
}
|
||||
|
||||
ArrangeJob::ArrangeJob() : m_plater{wxGetApp().plater()} { }
|
||||
|
||||
static std::string concat_strings(const std::set<std::string> &strings,
|
||||
const std::string &delim = "\n")
|
||||
{
|
||||
|
@ -605,9 +602,20 @@ static std::string concat_strings(const std::set<std::string> &strings,
|
|||
});
|
||||
}
|
||||
|
||||
void ArrangeJob::finalize() {
|
||||
// Ignore the arrange result if aborted.
|
||||
if (was_canceled()) return;
|
||||
void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (libnest2d::GeometryException &) {
|
||||
show_error(m_plater, _(L("Arrange failed. "
|
||||
"Found some exceptions when processing object geometries.")));
|
||||
eptr = nullptr;
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
|
||||
// Unprintable items go to the last virtual bed
|
||||
int beds = 0;
|
||||
|
@ -716,7 +724,6 @@ void ArrangeJob::finalize() {
|
|||
|
||||
m_plater->update();
|
||||
|
||||
Job::finalize();
|
||||
m_plater->m_arrange_running.store(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef ARRANGEJOB_HPP
|
||||
#define ARRANGEJOB_HPP
|
||||
|
||||
#include "PlaterJob.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "Job.hpp"
|
||||
#include "libslic3r/Arrange.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -12,7 +17,9 @@ class ModelInstance;
|
|||
|
||||
namespace GUI {
|
||||
|
||||
class ArrangeJob : public PlaterJob
|
||||
class Plater;
|
||||
|
||||
class ArrangeJob : public Job
|
||||
{
|
||||
using ArrangePolygon = arrangement::ArrangePolygon;
|
||||
using ArrangePolygons = arrangement::ArrangePolygons;
|
||||
|
@ -24,6 +31,10 @@ class ArrangeJob : public PlaterJob
|
|||
arrangement::ArrangeParams params;
|
||||
int current_plate_index = 0;
|
||||
Polygon bed_poly;
|
||||
Plater *m_plater;
|
||||
|
||||
// BBS: add flag for whether on current part plate
|
||||
bool only_on_partplate{false};
|
||||
|
||||
// clear m_selected and m_unselected, reserve space for next usage
|
||||
void clear_input();
|
||||
|
@ -42,26 +53,23 @@ class ArrangeJob : public PlaterJob
|
|||
|
||||
protected:
|
||||
|
||||
void prepare() override;
|
||||
|
||||
void check_unprintable();
|
||||
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
|
||||
void process() override;
|
||||
|
||||
public:
|
||||
ArrangeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
{}
|
||||
|
||||
int status_range() const override
|
||||
void prepare();
|
||||
|
||||
void process(Ctl &ctl) override;
|
||||
|
||||
ArrangeJob();
|
||||
|
||||
int status_range() const
|
||||
{
|
||||
// ensure finalize() is called after all operations in process() is finished.
|
||||
return int(m_selected.size() + m_unprintable.size() + 1);
|
||||
}
|
||||
|
||||
void finalize() override;
|
||||
void finalize(bool canceled, std::exception_ptr &e) override;
|
||||
};
|
||||
|
||||
std::optional<arrangement::ArrangePolygon> get_wipe_tower_arrangepoly(const Plater &);
|
||||
|
|
|
@ -12,12 +12,12 @@ wxDEFINE_EVENT(EVT_BIND_MACHINE_SUCCESS, wxCommandEvent);
|
|||
wxDEFINE_EVENT(EVT_BIND_MACHINE_FAIL, wxCommandEvent);
|
||||
|
||||
|
||||
static wxString waiting_auth_str = _L("Logging in");
|
||||
static wxString login_failed_str = _L("Login failed");
|
||||
static auto waiting_auth_str = _u8L("Logging in");
|
||||
static auto login_failed_str = _u8L("Login failed");
|
||||
|
||||
|
||||
BindJob::BindJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater, std::string dev_id, std::string dev_ip, std::string sec_link)
|
||||
: PlaterJob{std::move(pri), plater},
|
||||
BindJob::BindJob(std::string dev_id, std::string dev_ip, std::string sec_link)
|
||||
:
|
||||
m_dev_id(dev_id),
|
||||
m_dev_ip(dev_ip),
|
||||
m_sec_link(sec_link)
|
||||
|
@ -25,37 +25,27 @@ BindJob::BindJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater, std::st
|
|||
;
|
||||
}
|
||||
|
||||
void BindJob::on_exception(const std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &e) {
|
||||
PlaterJob::on_exception(eptr);
|
||||
}
|
||||
}
|
||||
|
||||
void BindJob::on_success(std::function<void()> success)
|
||||
{
|
||||
m_success_fun = success;
|
||||
}
|
||||
|
||||
void BindJob::update_status(int st, const wxString &msg)
|
||||
void BindJob::update_status(Ctl &ctl, int st, const std::string &msg)
|
||||
{
|
||||
GUI::Job::update_status(st, msg);
|
||||
ctl.update_status(st, msg);
|
||||
wxCommandEvent event(EVT_BIND_UPDATE_MESSAGE);
|
||||
event.SetString(msg);
|
||||
event.SetEventObject(m_event_handle);
|
||||
wxPostEvent(m_event_handle, event);
|
||||
}
|
||||
|
||||
void BindJob::process()
|
||||
void BindJob::process(Ctl &ctl)
|
||||
{
|
||||
int result_code = 0;
|
||||
std::string result_info;
|
||||
|
||||
/* display info */
|
||||
wxString msg = waiting_auth_str;
|
||||
auto msg = waiting_auth_str;
|
||||
int curr_percent = 0;
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
|
@ -67,40 +57,40 @@ void BindJob::process()
|
|||
std::string timezone = get_timezone_utc_hm(offset);
|
||||
|
||||
int result = m_agent->bind(m_dev_ip, m_dev_id, m_sec_link, timezone, m_improved,
|
||||
[this, &curr_percent, &msg, &result_code, &result_info](int stage, int code, std::string info) {
|
||||
[this, &ctl, &curr_percent, &msg, &result_code, &result_info](int stage, int code, std::string info) {
|
||||
|
||||
result_code = code;
|
||||
result_info = info;
|
||||
|
||||
if (stage == BBL::BindJobStage::LoginStageConnect) {
|
||||
curr_percent = 15;
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
} else if (stage == BBL::BindJobStage::LoginStageLogin) {
|
||||
curr_percent = 30;
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
} else if (stage == BBL::BindJobStage::LoginStageWaitForLogin) {
|
||||
curr_percent = 45;
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
} else if (stage == BBL::BindJobStage::LoginStageGetIdentify) {
|
||||
curr_percent = 60;
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
} else if (stage == BBL::BindJobStage::LoginStageWaitAuth) {
|
||||
curr_percent = 80;
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
} else if (stage == BBL::BindJobStage::LoginStageFinished) {
|
||||
curr_percent = 100;
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
} else {
|
||||
msg = _L("Logging in");
|
||||
msg = _u8L("Logging in");
|
||||
}
|
||||
|
||||
if (code != 0) {
|
||||
msg = _L("Login failed");
|
||||
msg = _u8L("Login failed");
|
||||
if (code == BAMBU_NETWORK_ERR_TIMEOUT) {
|
||||
msg += _L("Please check the printer network connection.");
|
||||
msg += _u8L("Please check the printer network connection.");
|
||||
}
|
||||
}
|
||||
update_status(curr_percent, msg);
|
||||
update_status(ctl, curr_percent, msg);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -138,11 +128,18 @@ void BindJob::process()
|
|||
return;
|
||||
}
|
||||
|
||||
void BindJob::finalize()
|
||||
void BindJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
if (was_canceled()) return;
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
eptr = nullptr;
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
Job::finalize();
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
}
|
||||
|
||||
void BindJob::set_event_handle(wxWindow *hanle)
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "PlaterJob.hpp"
|
||||
#include "Job.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class BindJob : public PlaterJob
|
||||
class BindJob : public Job
|
||||
{
|
||||
wxWindow * m_event_handle{nullptr};
|
||||
std::function<void()> m_success_fun{nullptr};
|
||||
|
@ -21,12 +21,10 @@ class BindJob : public PlaterJob
|
|||
int m_print_job_completed_id = 0;
|
||||
bool m_improved{false};
|
||||
|
||||
protected:
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
public:
|
||||
BindJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater, std::string dev_id, std::string dev_ip, std::string sec_link);
|
||||
BindJob(std::string dev_id, std::string dev_ip, std::string sec_link);
|
||||
|
||||
int status_range() const override
|
||||
int status_range() const
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
@ -34,9 +32,9 @@ public:
|
|||
bool is_finished() { return m_job_finished; }
|
||||
|
||||
void on_success(std::function<void()> success);
|
||||
void update_status(int st, const wxString &msg);
|
||||
void process() override;
|
||||
void finalize() override;
|
||||
void update_status(Ctl &ctl, int st, const std::string &msg);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
void set_event_handle(wxWindow* hanle);
|
||||
void post_fail_event(int code, std::string info);
|
||||
void set_improved(bool improved){m_improved = improved;};
|
||||
|
|
186
src/slic3r/GUI/Jobs/BoostThreadWorker.cpp
Normal file
186
src/slic3r/GUI/Jobs/BoostThreadWorker.cpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include <exception>
|
||||
|
||||
#include "BoostThreadWorker.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
void BoostThreadWorker::WorkerMessage::deliver(BoostThreadWorker &runner)
|
||||
{
|
||||
switch(MsgType(get_type())) {
|
||||
case Empty: break;
|
||||
case Status: {
|
||||
auto info = boost::get<StatusInfo>(m_data);
|
||||
if (runner.get_pri()) {
|
||||
runner.get_pri()->set_progress(info.status);
|
||||
runner.get_pri()->set_status_text(info.msg.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Finalize: {
|
||||
auto& entry = boost::get<JobEntry>(m_data);
|
||||
entry.job->finalize(entry.canceled, entry.eptr);
|
||||
|
||||
// Unhandled exceptions are rethrown without mercy.
|
||||
if (entry.eptr)
|
||||
std::rethrow_exception(entry.eptr);
|
||||
|
||||
break;
|
||||
}
|
||||
case MainThreadCall: {
|
||||
auto &calldata = boost::get<MainThreadCallData >(m_data);
|
||||
calldata.fn();
|
||||
calldata.promise.set_value();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoostThreadWorker::run()
|
||||
{
|
||||
bool stop = false;
|
||||
while (!stop) {
|
||||
m_input_queue
|
||||
.consume_one(BlockingWait{0, &m_running}, [this, &stop](JobEntry &e) {
|
||||
if (!e.job)
|
||||
stop = true;
|
||||
else {
|
||||
m_canceled.store(false);
|
||||
|
||||
try {
|
||||
e.job->process(*this);
|
||||
} catch (...) {
|
||||
e.eptr = std::current_exception();
|
||||
}
|
||||
|
||||
e.canceled = m_canceled.load();
|
||||
m_output_queue.push(std::move(e)); // finalization message
|
||||
}
|
||||
m_running.store(false);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void BoostThreadWorker::update_status(int st, const std::string &msg)
|
||||
{
|
||||
m_output_queue.push(st, msg);
|
||||
}
|
||||
|
||||
std::future<void> BoostThreadWorker::call_on_main_thread(std::function<void ()> fn)
|
||||
{
|
||||
MainThreadCallData cbdata{std::move(fn), {}};
|
||||
std::future<void> future = cbdata.promise.get_future();
|
||||
|
||||
m_output_queue.push(std::move(cbdata));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
BoostThreadWorker::BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
|
||||
boost::thread::attributes &attribs,
|
||||
const char * name)
|
||||
: m_progress(std::move(pri)), m_name{name}
|
||||
{
|
||||
if (m_progress)
|
||||
m_progress->set_cancel_callback([this](){ cancel(); });
|
||||
|
||||
m_thread = create_thread(attribs, [this] { this->run(); });
|
||||
|
||||
std::string nm{name};
|
||||
if (!nm.empty()) set_thread_name(m_thread, name);
|
||||
}
|
||||
|
||||
constexpr int ABORT_WAIT_MAX_MS = 10000;
|
||||
|
||||
BoostThreadWorker::~BoostThreadWorker()
|
||||
{
|
||||
bool joined = false;
|
||||
try {
|
||||
cancel_all();
|
||||
wait_for_idle(ABORT_WAIT_MAX_MS);
|
||||
m_input_queue.push(JobEntry{nullptr});
|
||||
joined = join(ABORT_WAIT_MAX_MS);
|
||||
} catch(...) {}
|
||||
|
||||
if (!joined)
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Could not join worker thread '" << m_name << "'";
|
||||
}
|
||||
|
||||
bool BoostThreadWorker::join(int timeout_ms)
|
||||
{
|
||||
if (!m_thread.joinable())
|
||||
return true;
|
||||
|
||||
if (timeout_ms <= 0) {
|
||||
m_thread.join();
|
||||
}
|
||||
else if (m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) {
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoostThreadWorker::process_events()
|
||||
{
|
||||
while (m_output_queue.consume_one([this](WorkerMessage &msg) {
|
||||
msg.deliver(*this);
|
||||
}));
|
||||
}
|
||||
|
||||
bool BoostThreadWorker::wait_for_current_job(unsigned timeout_ms)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (!is_idle()) {
|
||||
bool was_finish = false;
|
||||
bool timeout_reached = false;
|
||||
while (!timeout_reached && !was_finish) {
|
||||
timeout_reached =
|
||||
!m_output_queue.consume_one(BlockingWait{timeout_ms},
|
||||
[this, &was_finish](
|
||||
WorkerMessage &msg) {
|
||||
msg.deliver(*this);
|
||||
if (msg.get_type() ==
|
||||
WorkerMessage::Finalize)
|
||||
was_finish = true;
|
||||
});
|
||||
}
|
||||
|
||||
ret = !timeout_reached;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool BoostThreadWorker::wait_for_idle(unsigned timeout_ms)
|
||||
{
|
||||
bool timeout_reached = false;
|
||||
while (!timeout_reached && !is_idle()) {
|
||||
timeout_reached = !m_output_queue
|
||||
.consume_one(BlockingWait{timeout_ms},
|
||||
[this](WorkerMessage &msg) {
|
||||
msg.deliver(*this);
|
||||
});
|
||||
}
|
||||
|
||||
return !timeout_reached;
|
||||
}
|
||||
|
||||
bool BoostThreadWorker::push(std::unique_ptr<Job> job)
|
||||
{
|
||||
if (!job)
|
||||
return false;
|
||||
|
||||
m_input_queue.push(JobEntry{std::move(job)});
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
159
src/slic3r/GUI/Jobs/BoostThreadWorker.hpp
Normal file
159
src/slic3r/GUI/Jobs/BoostThreadWorker.hpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef BOOSTTHREADWORKER_HPP
|
||||
#define BOOSTTHREADWORKER_HPP
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "Worker.hpp"
|
||||
|
||||
#include <libslic3r/Thread.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "ThreadSafeQueue.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// An implementation of the Worker interface which uses the boost::thread
|
||||
// API and two thread safe message queues to communicate with the main thread
|
||||
// back and forth. The queue from the main thread to the worker thread holds the
|
||||
// job entries that will be performed on the worker. The other queue holds messages
|
||||
// from the worker to the main thread. These messages include status updates,
|
||||
// finishing operation and arbitrary functiors that need to be performed
|
||||
// on the main thread during the jobs execution, like displaying intermediate
|
||||
// results.
|
||||
class BoostThreadWorker : public Worker, private Job::Ctl
|
||||
{
|
||||
struct JobEntry // Goes into worker and also out of worker as a finalize msg
|
||||
{
|
||||
std::unique_ptr<Job> job;
|
||||
bool canceled = false;
|
||||
std::exception_ptr eptr = nullptr;
|
||||
};
|
||||
|
||||
// A message data for status updates. Only goes from worker to main thread.
|
||||
struct StatusInfo { int status; std::string msg; };
|
||||
|
||||
// An arbitrary callback to be called on the main thread. Only from worker
|
||||
// to main thread.
|
||||
struct MainThreadCallData
|
||||
{
|
||||
std::function<void()> fn;
|
||||
std::promise<void> promise;
|
||||
};
|
||||
|
||||
struct EmptyMessage {};
|
||||
|
||||
class WorkerMessage
|
||||
{
|
||||
public:
|
||||
enum MsgType { Empty, Status, Finalize, MainThreadCall };
|
||||
|
||||
private:
|
||||
boost::variant<EmptyMessage, StatusInfo, JobEntry, MainThreadCallData> m_data;
|
||||
|
||||
public:
|
||||
WorkerMessage() = default;
|
||||
WorkerMessage(int s, std::string txt)
|
||||
: m_data{StatusInfo{s, std::move(txt)}}
|
||||
{}
|
||||
WorkerMessage(JobEntry &&entry) : m_data{std::move(entry)} {}
|
||||
WorkerMessage(MainThreadCallData fn) : m_data{std::move(fn)} {}
|
||||
|
||||
int get_type () const { return m_data.which(); }
|
||||
|
||||
void deliver(BoostThreadWorker &runner);
|
||||
};
|
||||
|
||||
using JobQueue = ThreadSafeQueueSPSC<JobEntry>;
|
||||
using MessageQueue = ThreadSafeQueueSPSC<WorkerMessage>;
|
||||
|
||||
boost::thread m_thread;
|
||||
std::atomic<bool> m_running{false}, m_canceled{false};
|
||||
std::shared_ptr<ProgressIndicator> m_progress;
|
||||
JobQueue m_input_queue; // from main thread to worker
|
||||
MessageQueue m_output_queue; // form worker to main thread
|
||||
std::string m_name;
|
||||
|
||||
void run();
|
||||
|
||||
bool join(int timeout_ms = 0);
|
||||
|
||||
protected:
|
||||
// Implement Job::Ctl interface:
|
||||
|
||||
void update_status(int st, const std::string &msg = "") override;
|
||||
|
||||
bool was_canceled() const override { return m_canceled.load(); }
|
||||
|
||||
std::future<void> call_on_main_thread(std::function<void()> fn) override;
|
||||
|
||||
public:
|
||||
explicit BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
|
||||
boost::thread::attributes & attr,
|
||||
const char * name = "");
|
||||
|
||||
explicit BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
|
||||
boost::thread::attributes && attr,
|
||||
const char * name = "")
|
||||
: BoostThreadWorker{std::move(pri), attr, name}
|
||||
{}
|
||||
|
||||
explicit BoostThreadWorker(std::shared_ptr<ProgressIndicator> pri,
|
||||
const char * name = "")
|
||||
: BoostThreadWorker{std::move(pri), {}, name}
|
||||
{}
|
||||
|
||||
~BoostThreadWorker();
|
||||
|
||||
BoostThreadWorker(const BoostThreadWorker &) = delete;
|
||||
BoostThreadWorker(BoostThreadWorker &&) = delete;
|
||||
BoostThreadWorker &operator=(const BoostThreadWorker &) = delete;
|
||||
BoostThreadWorker &operator=(BoostThreadWorker &&) = delete;
|
||||
|
||||
bool push(std::unique_ptr<Job> job) override;
|
||||
|
||||
bool is_idle() const override
|
||||
{
|
||||
// The assumption is that jobs can only be queued from a single main
|
||||
// thread from which this method is also called. And the output
|
||||
// messages are also processed only in this calling thread. In that
|
||||
// case, if the input queue is empty, it will remain so during this
|
||||
// function call. If the worker thread is also not running and the
|
||||
// output queue is already processed, we can safely say that the
|
||||
// worker is dormant.
|
||||
return m_input_queue.empty() && !m_running.load() && m_output_queue.empty();
|
||||
}
|
||||
|
||||
void cancel() override { m_canceled.store(true); }
|
||||
void cancel_all() override { m_input_queue.clear(); cancel(); }
|
||||
|
||||
ProgressIndicator * get_pri() { return m_progress.get(); }
|
||||
const ProgressIndicator * get_pri() const { return m_progress.get(); }
|
||||
|
||||
void clear_percent() override
|
||||
{
|
||||
if (m_progress) {
|
||||
m_progress->clear_percent();
|
||||
}
|
||||
}
|
||||
|
||||
void show_error_info(const std::string &msg, int code, const std::string &description, const std::string &extra) override
|
||||
{
|
||||
if (m_progress) {
|
||||
m_progress->show_error_info(from_u8(msg), code, from_u8(description), from_u8(extra));
|
||||
}
|
||||
}
|
||||
|
||||
void process_events() override;
|
||||
bool wait_for_current_job(unsigned timeout_ms = 0) override;
|
||||
bool wait_for_idle(unsigned timeout_ms = 0) override;
|
||||
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // BOOSTTHREADWORKER_HPP
|
57
src/slic3r/GUI/Jobs/BusyCursorJob.hpp
Normal file
57
src/slic3r/GUI/Jobs/BusyCursorJob.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2022 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef BUSYCURSORJOB_HPP
|
||||
#define BUSYCURSORJOB_HPP
|
||||
|
||||
#include "Job.hpp"
|
||||
|
||||
#include <wx/utils.h>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
struct CursorSetterRAII
|
||||
{
|
||||
Job::Ctl &ctl;
|
||||
CursorSetterRAII(Job::Ctl &c) : ctl{c}
|
||||
{
|
||||
ctl.call_on_main_thread([] { wxBeginBusyCursor(); });
|
||||
}
|
||||
~CursorSetterRAII()
|
||||
{
|
||||
try {
|
||||
ctl.call_on_main_thread([] { wxEndBusyCursor(); });
|
||||
} catch(...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Can't revert cursor from busy to normal";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class JobSubclass>
|
||||
class BusyCursored: public Job {
|
||||
JobSubclass m_job;
|
||||
|
||||
public:
|
||||
template<class... Args>
|
||||
BusyCursored(Args &&...args) : m_job{std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
void process(Ctl &ctl) override
|
||||
{
|
||||
CursorSetterRAII cursor_setter{ctl};
|
||||
m_job.process(ctl);
|
||||
}
|
||||
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override
|
||||
{
|
||||
m_job.finalize(canceled, eptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BUSYCURSORJOB_HPP
|
170
src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
Normal file
170
src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
///|/ Copyright (c) Prusa Research 2022 Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "CreateFontNameImageJob.hpp"
|
||||
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
// rasterization of ExPoly
|
||||
#include "libslic3r/SLA/AGGRaster.hpp"
|
||||
|
||||
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||
|
||||
// ability to request new frame after finish rendering
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
|
||||
#include "wx/fontenum.h"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
const std::string CreateFontImageJob::default_text = "AaBbCc 123";
|
||||
|
||||
CreateFontImageJob::CreateFontImageJob(FontImageData &&input)
|
||||
: m_input(std::move(input))
|
||||
{
|
||||
assert(wxFontEnumerator::IsValidFacename(m_input.font_name));
|
||||
assert(m_input.gray_level > 0 && m_input.gray_level < 255);
|
||||
assert(m_input.texture_id != 0);
|
||||
}
|
||||
|
||||
void CreateFontImageJob::process(Ctl &ctl)
|
||||
{
|
||||
if (!wxFontEnumerator::IsValidFacename(m_input.font_name)) return;
|
||||
// Select font
|
||||
wxFont wx_font(
|
||||
wxFontInfo().FaceName(m_input.font_name).Encoding(m_input.encoding));
|
||||
if (!wx_font.IsOk()) return;
|
||||
|
||||
std::unique_ptr<Emboss::FontFile> font_file =
|
||||
WxFontUtils::create_font_file(wx_font);
|
||||
if (font_file == nullptr) return;
|
||||
|
||||
Emboss::FontFileWithCache font_file_with_cache(std::move(font_file));
|
||||
// use only first line of text
|
||||
std::string& text = m_input.text;
|
||||
if (text.empty())
|
||||
text = default_text; // copy
|
||||
|
||||
size_t enter_pos = text.find('\n');
|
||||
if (enter_pos < text.size()) {
|
||||
// text start with enter
|
||||
if (enter_pos == 0) return;
|
||||
// exist enter, soo delete all after enter
|
||||
text = text.substr(0, enter_pos);
|
||||
}
|
||||
|
||||
std::function<bool()> was_canceled = [&ctl, cancel = m_input.cancel]() -> bool {
|
||||
if (ctl.was_canceled()) return true;
|
||||
if (cancel->load()) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
FontProp fp; // create default font parameters
|
||||
ExPolygons shapes = Emboss::text2shapes(font_file_with_cache, text.c_str(), fp, was_canceled);
|
||||
|
||||
// select some character from font e.g. default text
|
||||
if (shapes.empty())
|
||||
shapes = Emboss::text2shapes(font_file_with_cache, default_text.c_str(), fp, was_canceled);
|
||||
if (shapes.empty()) {
|
||||
m_input.cancel->store(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// normalize height of font
|
||||
BoundingBox bounding_box;
|
||||
for (const ExPolygon &shape : shapes)
|
||||
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||
if (bounding_box.size().x() < 1 || bounding_box.size().y() < 1) {
|
||||
m_input.cancel->store(true);
|
||||
return;
|
||||
}
|
||||
double scale = m_input.size.y() / (double) bounding_box.size().y();
|
||||
BoundingBoxf bb2(bounding_box.min.cast<double>(),
|
||||
bounding_box.max.cast<double>());
|
||||
bb2.scale(scale);
|
||||
Vec2d size_f = bb2.size();
|
||||
m_tex_size = Point(std::ceil(size_f.x()), std::ceil(size_f.y()));
|
||||
// crop image width
|
||||
if (m_tex_size.x() > m_input.size.x()) m_tex_size.x() = m_input.size.x();
|
||||
if (m_tex_size.y() > m_input.size.y()) m_tex_size.y() = m_input.size.y();
|
||||
|
||||
// Set up result
|
||||
unsigned bit_count = 4; // RGBA
|
||||
m_result = std::vector<unsigned char>(m_tex_size.x() * m_tex_size.y() * bit_count, {255});
|
||||
|
||||
sla::Resolution resolution(m_tex_size.x(), m_tex_size.y());
|
||||
double pixel_dim = SCALING_FACTOR / scale;
|
||||
sla::PixelDim dim(pixel_dim, pixel_dim);
|
||||
double gamma = 1.;
|
||||
std::unique_ptr<sla::RasterBase> r =
|
||||
sla::create_raster_grayscale_aa(resolution, dim, gamma);
|
||||
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||
for (const ExPolygon &shape : shapes) r->draw(shape);
|
||||
|
||||
// copy rastered data to pixels
|
||||
sla::RasterEncoder encoder =
|
||||
[&pix = m_result, w = m_tex_size.x(), h = m_tex_size.y(),
|
||||
gray_level = m_input.gray_level]
|
||||
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
||||
size_t size {static_cast<size_t>(w*h)};
|
||||
const unsigned char *ptr2 = (const unsigned char *) ptr;
|
||||
for (size_t x = 0; x < width; ++x)
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
size_t index = y*w + x;
|
||||
assert(index < size);
|
||||
if (index >= size) continue;
|
||||
pix[3+4*index] = ptr2[y * width + x] / gray_level;
|
||||
}
|
||||
return sla::EncodedRaster();
|
||||
};
|
||||
r->encode(encoder);
|
||||
}
|
||||
|
||||
void CreateFontImageJob::finalize(bool canceled, std::exception_ptr &)
|
||||
{
|
||||
if (m_input.count_opened_font_files)
|
||||
--(*m_input.count_opened_font_files);
|
||||
if (canceled || m_input.cancel->load()) return;
|
||||
|
||||
*m_input.is_created = true;
|
||||
|
||||
// Exist result bitmap with preview?
|
||||
// (not valid input. e.g. not loadable font)
|
||||
if (m_result.empty()) {
|
||||
// TODO: write text cannot load into texture
|
||||
m_result = std::vector<unsigned char>(m_tex_size.x() * m_tex_size.y() * 4, {255});
|
||||
}
|
||||
|
||||
// upload texture on GPU
|
||||
const GLenum target = GL_TEXTURE_2D;
|
||||
glsafe(::glBindTexture(target, m_input.texture_id));
|
||||
|
||||
GLsizei w = m_tex_size.x(), h = m_tex_size.y();
|
||||
GLint xoffset = m_input.size.x() - m_tex_size.x(), // arrange right
|
||||
yoffset = m_input.size.y() * m_input.index;
|
||||
glsafe(::glTexSubImage2D(target, m_input.level, xoffset, yoffset, w, h,
|
||||
m_input.format, m_input.type, m_result.data()));
|
||||
|
||||
// clear rest of texture
|
||||
std::vector<unsigned char> empty_data(xoffset * h * 4, {0});
|
||||
glsafe(::glTexSubImage2D(target, m_input.level, 0, yoffset, xoffset, h,
|
||||
m_input.format, m_input.type, empty_data.data()));
|
||||
|
||||
// bind default texture
|
||||
GLuint no_texture_id = 0;
|
||||
glsafe(::glBindTexture(target, no_texture_id));
|
||||
|
||||
// show rendered texture
|
||||
wxGetApp().plater()->canvas3D()->schedule_extra_frame(0);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Generate Preview font('" << m_input.font_name << "' id:" << m_input.index << ") "
|
||||
<< "with text: '" << m_input.text << "' "
|
||||
<< "texture_size " << m_input.size.x() << " x " << m_input.size.y();
|
||||
}
|
85
src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp
Normal file
85
src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
///|/ Copyright (c) Prusa Research 2022 Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_CreateFontNameImageJob_hpp_
|
||||
#define slic3r_CreateFontNameImageJob_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <GL/glew.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/fontenc.h>
|
||||
#include "Job.hpp"
|
||||
#include "libslic3r/Point.hpp" // Vec2i
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
/// <summary>
|
||||
/// Keep data for rasterization of text by font face
|
||||
/// </summary>
|
||||
struct FontImageData
|
||||
{
|
||||
// Text to rasterize
|
||||
std::string text;
|
||||
// Define font face
|
||||
wxString font_name;
|
||||
wxFontEncoding encoding;
|
||||
// texture for copy result to
|
||||
// texture MUST BE initialized
|
||||
GLuint texture_id;
|
||||
// Index of face name, define place in texture
|
||||
size_t index;
|
||||
// Height of each text
|
||||
// And Limit for width
|
||||
Vec2i size; // in px
|
||||
|
||||
// bigger value create darker image
|
||||
// divide value 255
|
||||
unsigned char gray_level = 5;
|
||||
|
||||
// texture meta data
|
||||
GLenum format = GL_ALPHA, type = GL_UNSIGNED_BYTE;
|
||||
GLint level = 0;
|
||||
|
||||
// prevent opening too much files
|
||||
// it is decreased in finalize phase
|
||||
unsigned int *count_opened_font_files = nullptr;
|
||||
|
||||
std::shared_ptr<std::atomic<bool>> cancel = nullptr;
|
||||
std::shared_ptr<bool> is_created = nullptr;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create image for face name
|
||||
/// </summary>
|
||||
class CreateFontImageJob : public Job
|
||||
{
|
||||
FontImageData m_input;
|
||||
std::vector<unsigned char> m_result;
|
||||
Point m_tex_size;
|
||||
public:
|
||||
CreateFontImageJob(FontImageData &&input);
|
||||
/// <summary>
|
||||
/// Rasterize text into image (result)
|
||||
/// </summary>
|
||||
/// <param name="ctl">Check for cancelation</param>
|
||||
void process(Ctl &ctl) override;
|
||||
|
||||
/// <summary>
|
||||
/// Copy image data into OpenGL texture
|
||||
/// </summary>
|
||||
/// <param name="canceled"></param>
|
||||
/// <param name=""></param>
|
||||
void finalize(bool canceled, std::exception_ptr &) override;
|
||||
|
||||
/// <summary>
|
||||
/// Text used for generate preview for empty text
|
||||
/// and when no glyph for given m_input.text
|
||||
/// </summary>
|
||||
static const std::string default_text;
|
||||
};
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_CreateFontNameImageJob_hpp_
|
157
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp
Normal file
157
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
///|/ Copyright (c) Prusa Research 2022 Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "CreateFontStyleImagesJob.hpp"
|
||||
|
||||
// rasterization of ExPoly
|
||||
#include "libslic3r/SLA/AGGRaster.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||
|
||||
// ability to request new frame after finish rendering
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Emboss;
|
||||
using namespace Slic3r::GUI;
|
||||
using namespace Slic3r::GUI::Emboss;
|
||||
|
||||
|
||||
CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input)
|
||||
: m_input(std::move(input)), m_width(0), m_height(0)
|
||||
{
|
||||
assert(m_input.result != nullptr);
|
||||
assert(!m_input.styles.empty());
|
||||
assert(!m_input.text.empty());
|
||||
assert(m_input.max_size.x() > 1);
|
||||
assert(m_input.max_size.y() > 1);
|
||||
assert(m_input.ppm > 1e-5);
|
||||
}
|
||||
|
||||
void CreateFontStyleImagesJob::process(Ctl &ctl)
|
||||
{
|
||||
// create shapes and calc size (bounding boxes)
|
||||
std::vector<ExPolygons> name_shapes(m_input.styles.size());
|
||||
std::vector<double> scales(m_input.styles.size());
|
||||
m_images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
|
||||
|
||||
auto was_canceled = []() { return false; };
|
||||
for (auto &item : m_input.styles) {
|
||||
size_t index = &item - &m_input.styles.front();
|
||||
ExPolygons &shapes = name_shapes[index];
|
||||
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop, was_canceled);
|
||||
|
||||
// create image description
|
||||
StyleManager::StyleImage &image = m_images[index];
|
||||
BoundingBox &bounding_box = image.bounding_box;
|
||||
for (ExPolygon &shape : shapes)
|
||||
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||
|
||||
// calculate conversion from FontPoint to screen pixels by size of font
|
||||
double scale = get_text_shape_scale(item.prop, *item.font.font_file);
|
||||
scales[index] = scale;
|
||||
|
||||
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
||||
BoundingBoxf bb2(bounding_box.min.cast<double>(),
|
||||
bounding_box.max.cast<double>());
|
||||
bb2.scale(scale);
|
||||
image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x());
|
||||
image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y());
|
||||
|
||||
// crop image width
|
||||
if (image.tex_size.x > m_input.max_size.x())
|
||||
image.tex_size.x = m_input.max_size.x();
|
||||
// crop image height
|
||||
if (image.tex_size.y > m_input.max_size.y())
|
||||
image.tex_size.y = m_input.max_size.y();
|
||||
}
|
||||
|
||||
// arrange bounding boxes
|
||||
int offset_y = 0;
|
||||
m_width = 0;
|
||||
for (StyleManager::StyleImage &image : m_images) {
|
||||
image.offset.y() = offset_y;
|
||||
offset_y += image.tex_size.y+1;
|
||||
if (m_width < image.tex_size.x)
|
||||
m_width = image.tex_size.x;
|
||||
}
|
||||
m_height = offset_y;
|
||||
for (StyleManager::StyleImage &image : m_images) {
|
||||
const Point &o = image.offset;
|
||||
const ImVec2 &s = image.tex_size;
|
||||
image.uv0 = ImVec2(o.x() / (double) m_width,
|
||||
o.y() / (double) m_height);
|
||||
image.uv1 = ImVec2((o.x() + s.x) / (double) m_width,
|
||||
(o.y() + s.y) / (double) m_height);
|
||||
}
|
||||
|
||||
// Set up result
|
||||
m_pixels = std::vector<unsigned char>(4 * m_width * m_height, {255});
|
||||
|
||||
// upload sub textures
|
||||
for (StyleManager::StyleImage &image : m_images) {
|
||||
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
|
||||
size_t index = &image - &m_images.front();
|
||||
double pixel_dim = SCALING_FACTOR / scales[index];
|
||||
sla::PixelDim dim(pixel_dim, pixel_dim);
|
||||
double gamma = 1.;
|
||||
std::unique_ptr<sla::RasterBase> r =
|
||||
sla::create_raster_grayscale_aa(resolution, dim, gamma);
|
||||
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
|
||||
|
||||
// copy rastered data to pixels
|
||||
sla::RasterEncoder encoder = [&offset = image.offset, &pix = m_pixels, w=m_width,h=m_height]
|
||||
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
||||
// bigger value create darker image
|
||||
unsigned char gray_level = 1;
|
||||
size_t size {static_cast<size_t>(w*h)};
|
||||
assert((offset.x() + width) <= (size_t)w);
|
||||
assert((offset.y() + height) <= (size_t)h);
|
||||
const unsigned char *ptr2 = (const unsigned char *) ptr;
|
||||
for (size_t x = 0; x < width; ++x)
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
size_t index = (offset.y() + y)*w + offset.x() + x;
|
||||
assert(index < size);
|
||||
if (index >= size) continue;
|
||||
pix[4*index+3] = ptr2[y * width + x] / gray_level;
|
||||
}
|
||||
return sla::EncodedRaster();
|
||||
};
|
||||
r->encode(encoder);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
|
||||
{
|
||||
if (canceled) return;
|
||||
// upload texture on GPU
|
||||
GLuint tex_id;
|
||||
GLenum target = GL_TEXTURE_2D, format = GL_RGBA, type = GL_UNSIGNED_BYTE;
|
||||
GLint level = 0, border = 0;
|
||||
glsafe(::glGenTextures(1, &tex_id));
|
||||
glsafe(::glBindTexture(target, tex_id));
|
||||
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||
GLint w = m_width, h = m_height;
|
||||
glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type,
|
||||
(const void *) m_pixels.data()));
|
||||
|
||||
// set up texture id
|
||||
void *texture_id = (void *) (intptr_t) tex_id;
|
||||
for (StyleManager::StyleImage &image : m_images)
|
||||
image.texture_id = texture_id;
|
||||
|
||||
// move to result
|
||||
m_input.result->styles = std::move(m_input.styles);
|
||||
m_input.result->images = std::move(m_images);
|
||||
|
||||
// bind default texture
|
||||
GLuint no_texture_id = 0;
|
||||
glsafe(::glBindTexture(target, no_texture_id));
|
||||
|
||||
// show rendered texture
|
||||
wxGetApp().plater()->canvas3D()->schedule_extra_frame(0);
|
||||
}
|
40
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp
Normal file
40
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
///|/ Copyright (c) Prusa Research 2022 Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_CreateFontStyleImagesJob_hpp_
|
||||
#define slic3r_CreateFontStyleImagesJob_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <libslic3r/Emboss.hpp>
|
||||
#include "slic3r/Utils/EmbossStyleManager.hpp"
|
||||
#include "Job.hpp"
|
||||
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
|
||||
/// <summary>
|
||||
/// Create texture with name of styles written by its style
|
||||
/// NOTE: Access to glyph cache is possible only from job
|
||||
/// </summary>
|
||||
class CreateFontStyleImagesJob : public Job
|
||||
{
|
||||
StyleManager::StyleImagesData m_input;
|
||||
|
||||
// Output data
|
||||
// texture size
|
||||
int m_width, m_height;
|
||||
// texture data
|
||||
std::vector<unsigned char> m_pixels;
|
||||
// descriptors of sub textures
|
||||
std::vector<StyleManager::StyleImage> m_images;
|
||||
|
||||
public:
|
||||
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_CreateFontStyleImagesJob_hpp_
|
1568
src/slic3r/GUI/Jobs/EmbossJob.cpp
Normal file
1568
src/slic3r/GUI/Jobs/EmbossJob.cpp
Normal file
File diff suppressed because it is too large
Load diff
265
src/slic3r/GUI/Jobs/EmbossJob.hpp
Normal file
265
src/slic3r/GUI/Jobs/EmbossJob.hpp
Normal file
|
@ -0,0 +1,265 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2022 Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_EmbossJob_hpp_
|
||||
#define slic3r_EmbossJob_hpp_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <libslic3r/Emboss.hpp>
|
||||
#include <libslic3r/EmbossShape.hpp> // ExPolygonsWithIds
|
||||
#include "libslic3r/Point.hpp" // Transform3d
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/TextLines.hpp"
|
||||
|
||||
#include "Job.hpp"
|
||||
|
||||
// forward declarations
|
||||
namespace Slic3r {
|
||||
class TriangleMesh;
|
||||
class ModelVolume;
|
||||
enum class ModelVolumeType : int;
|
||||
class BuildVolume;
|
||||
namespace GUI {
|
||||
class RaycastManager;
|
||||
class Plater;
|
||||
class GLCanvas3D;
|
||||
class Worker;
|
||||
class Selection;
|
||||
}}
|
||||
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
|
||||
/// <summary>
|
||||
/// Base data hold data for create emboss shape
|
||||
/// </summary>
|
||||
class DataBase
|
||||
{
|
||||
public:
|
||||
DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel)
|
||||
: volume_name(volume_name), cancel(std::move(cancel)) {}
|
||||
DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel, EmbossShape&& shape)
|
||||
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape)){}
|
||||
DataBase(DataBase &&) = default;
|
||||
virtual ~DataBase() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Create shape
|
||||
/// e.g. Text extract glyphs from font
|
||||
/// Not 'const' function because it could modify shape
|
||||
/// </summary>
|
||||
virtual EmbossShape& create_shape() { return shape; };
|
||||
|
||||
/// <summary>
|
||||
/// Write data how to reconstruct shape to volume
|
||||
/// </summary>
|
||||
/// <param name="volume">Data object for store emboss params</param>
|
||||
virtual void write(ModelVolume &volume) const;
|
||||
|
||||
// Define projection move
|
||||
// True (raised) .. move outside from surface (MODEL_PART)
|
||||
// False (engraved).. move into object (NEGATIVE_VOLUME)
|
||||
bool is_outside = true;
|
||||
|
||||
// Define per letter projection on one text line
|
||||
// [optional] It is not used when empty
|
||||
Slic3r::Emboss::TextLines text_lines = {};
|
||||
|
||||
// [optional] Define distance for surface
|
||||
// It is used only for flat surface (not cutted)
|
||||
// Position of Zero(not set value) differ for MODEL_PART and NEGATIVE_VOLUME
|
||||
std::optional<float> from_surface;
|
||||
|
||||
// new volume name
|
||||
std::string volume_name;
|
||||
|
||||
// flag that job is canceled
|
||||
// for time after process.
|
||||
std::shared_ptr<std::atomic<bool>> cancel;
|
||||
|
||||
// shape to emboss
|
||||
EmbossShape shape;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to create ModelVolume in job
|
||||
/// Volume is created on the surface of existing volume in object.
|
||||
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
|
||||
/// </summary>
|
||||
struct DataCreateVolume : public DataBase
|
||||
{
|
||||
// define embossed volume type
|
||||
ModelVolumeType volume_type;
|
||||
|
||||
// parent ModelObject index where to create volume
|
||||
ObjectID object_id;
|
||||
|
||||
// new created volume transformation
|
||||
Transform3d trmat;
|
||||
};
|
||||
using DataBasePtr = std::unique_ptr<DataBase>;
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to update embossed text object in job
|
||||
/// </summary>
|
||||
struct DataUpdate
|
||||
{
|
||||
// Hold data about shape
|
||||
DataBasePtr base;
|
||||
|
||||
// unique identifier of volume to change
|
||||
ObjectID volume_id;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Update text shape in existing text volume
|
||||
/// Predict that there is only one runnig(not canceled) instance of it
|
||||
/// </summary>
|
||||
class UpdateJob : public Job
|
||||
{
|
||||
DataUpdate m_input;
|
||||
TriangleMesh m_result;
|
||||
|
||||
public:
|
||||
// move params to private variable
|
||||
explicit UpdateJob(DataUpdate &&input);
|
||||
|
||||
/// <summary>
|
||||
/// Create new embossed volume by m_input data and store to m_result
|
||||
/// </summary>
|
||||
/// <param name="ctl">Control containing cancel flag</param>
|
||||
void process(Ctl &ctl) override;
|
||||
|
||||
/// <summary>
|
||||
/// Update volume - change object_id
|
||||
/// </summary>
|
||||
/// <param name="canceled">Was process canceled.
|
||||
/// NOTE: Be carefull it doesn't care about
|
||||
/// time between finished process and started finalize part.</param>
|
||||
/// <param name="">unused</param>
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
|
||||
/// <summary>
|
||||
/// Update text volume
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume to be updated</param>
|
||||
/// <param name="mesh">New Triangle mesh for volume</param>
|
||||
/// <param name="base">Data to write into volume</param>
|
||||
static void update_volume(ModelVolume *volume, TriangleMesh &&mesh, const DataBase &base);
|
||||
};
|
||||
|
||||
struct SurfaceVolumeData
|
||||
{
|
||||
// Transformation of volume inside of object
|
||||
Transform3d transform;
|
||||
|
||||
struct ModelSource
|
||||
{
|
||||
// source volumes
|
||||
std::shared_ptr<const TriangleMesh> mesh;
|
||||
// Transformation of volume inside of object
|
||||
Transform3d tr;
|
||||
};
|
||||
using ModelSources = std::vector<ModelSource>;
|
||||
ModelSources sources;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to update embossed text object in job
|
||||
/// </summary>
|
||||
struct UpdateSurfaceVolumeData : public DataUpdate, public SurfaceVolumeData{};
|
||||
|
||||
/// <summary>
|
||||
/// Update text volume to use surface from object
|
||||
/// </summary>
|
||||
class UpdateSurfaceVolumeJob : public Job
|
||||
{
|
||||
UpdateSurfaceVolumeData m_input;
|
||||
TriangleMesh m_result;
|
||||
|
||||
public:
|
||||
// move params to private variable
|
||||
explicit UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Copied triangles from object to be able create mesh for cut surface from
|
||||
/// </summary>
|
||||
/// <param name="volume">Define embossed volume</param>
|
||||
/// <returns>Source data for cut surface from</returns>
|
||||
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &volume);
|
||||
|
||||
/// <summary>
|
||||
/// shorten params for start_crate_volume functions
|
||||
/// </summary>
|
||||
struct CreateVolumeParams
|
||||
{
|
||||
GLCanvas3D &canvas;
|
||||
|
||||
// Direction of ray into scene
|
||||
const Camera &camera;
|
||||
|
||||
// To put new object on the build volume
|
||||
const BuildVolume &build_volume;
|
||||
|
||||
// used to emplace job for execution
|
||||
Worker &worker;
|
||||
|
||||
// New created volume type
|
||||
ModelVolumeType volume_type;
|
||||
|
||||
// Contain AABB trees from scene
|
||||
RaycastManager &raycaster;
|
||||
|
||||
// Define which gizmo open on the success
|
||||
unsigned char gizmo; // GLGizmosManager::EType
|
||||
|
||||
// Volume define object to add new volume
|
||||
const GLVolume *gl_volume;
|
||||
|
||||
// Wanted additionl move in Z(emboss) direction of new created volume
|
||||
std::optional<float> distance = {};
|
||||
|
||||
// Wanted additionl rotation around Z of new created volume
|
||||
std::optional<float> angle = {};
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create new volume on position of mouse cursor
|
||||
/// </summary>
|
||||
/// <param name="plater_ptr">canvas + camera + bed shape + </param>
|
||||
/// <param name="data">Shape of emboss</param>
|
||||
/// <param name="volume_type">New created volume type</param>
|
||||
/// <param name="raycaster">Knows object in scene</param>
|
||||
/// <param name="gizmo">Define which gizmo open on the success - enum GLGizmosManager::EType</param>
|
||||
/// <param name="mouse_pos">Define position where to create volume</param>
|
||||
/// <param name="distance">Wanted additionl move in Z(emboss) direction of new created volume</param>
|
||||
/// <param name="angle">Wanted additionl rotation around Z of new created volume</param>
|
||||
/// <returns>True on success otherwise False</returns>
|
||||
bool start_create_volume(CreateVolumeParams &input, DataBasePtr data, const Vec2d &mouse_pos);
|
||||
|
||||
/// <summary>
|
||||
/// Same as previous function but without mouse position
|
||||
/// Need to suggest position or put near the selection
|
||||
/// </summary>
|
||||
bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr data);
|
||||
|
||||
/// <summary>
|
||||
/// Start job for update embossed volume
|
||||
/// </summary>
|
||||
/// <param name="data">define update data</param>
|
||||
/// <param name="volume">Volume to be updated</param>
|
||||
/// <param name="selection">Keep model and gl_volumes - when start use surface volume must be selected</param>
|
||||
/// <param name="raycaster">Could cast ray to scene</param>
|
||||
/// <returns>True when start job otherwise false</returns>
|
||||
bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Selection &selection, RaycastManager &raycaster);
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_EmbossJob_hpp_
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "FillBedJob.hpp"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
@ -198,8 +202,12 @@ void FillBedJob::prepare()
|
|||
p.translation(X) -= p.bed_idx * stride;*/
|
||||
}
|
||||
|
||||
void FillBedJob::process()
|
||||
void FillBedJob::process(Ctl &ctl)
|
||||
{
|
||||
auto statustxt = _u8L("Filling");
|
||||
ctl.call_on_main_thread([this] { prepare(); }).wait();
|
||||
ctl.update_status(0, statustxt);
|
||||
|
||||
if (m_object_idx == -1 || m_selected.empty()) return;
|
||||
|
||||
update_arrange_params(params, m_plater->config(), m_selected);
|
||||
|
@ -217,13 +225,13 @@ void FillBedJob::process()
|
|||
update_unselected_items_inflation(m_unselected, m_plater->config(), params);
|
||||
|
||||
bool do_stop = false;
|
||||
params.stopcondition = [this, &do_stop]() {
|
||||
return was_canceled() || do_stop;
|
||||
params.stopcondition = [&ctl, &do_stop]() {
|
||||
return ctl.was_canceled() || do_stop;
|
||||
};
|
||||
|
||||
params.progressind = [this](unsigned st,std::string str="") {
|
||||
params.progressind = [this, &ctl, &statustxt](unsigned st,std::string str="") {
|
||||
if (st > 0)
|
||||
update_status(st, _L("Filling") + " " + wxString::FromUTF8(str));
|
||||
ctl.update_status(st * 100 / status_range(), statustxt + " " + str);
|
||||
};
|
||||
|
||||
params.on_packed = [&do_stop] (const ArrangePolygon &ap) {
|
||||
|
@ -235,15 +243,18 @@ void FillBedJob::process()
|
|||
arrangement::arrange(m_selected, m_unselected, m_bedpts, params);
|
||||
|
||||
// finalize just here.
|
||||
update_status(m_status_range, was_canceled() ?
|
||||
_L("Bed filling canceled.") :
|
||||
_L("Bed filling done."));
|
||||
ctl.update_status(100, ctl.was_canceled() ?
|
||||
_u8L("Bed filling canceled.") :
|
||||
_u8L("Bed filling done."));
|
||||
}
|
||||
|
||||
void FillBedJob::finalize()
|
||||
FillBedJob::FillBedJob() : m_plater{wxGetApp().plater()} {}
|
||||
|
||||
void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
// Ignore the arrange result if aborted.
|
||||
if (was_canceled()) return;
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
|
||||
if (m_object_idx == -1) return;
|
||||
|
||||
|
@ -304,8 +315,6 @@ void FillBedJob::finalize()
|
|||
|
||||
m_plater->update();
|
||||
}
|
||||
|
||||
Job::finalize();
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef FILLBEDJOB_HPP
|
||||
#define FILLBEDJOB_HPP
|
||||
|
||||
|
@ -7,7 +11,7 @@ namespace Slic3r { namespace GUI {
|
|||
|
||||
class Plater;
|
||||
|
||||
class FillBedJob : public PlaterJob
|
||||
class FillBedJob : public Job
|
||||
{
|
||||
int m_object_idx = -1;
|
||||
|
||||
|
@ -24,23 +28,21 @@ class FillBedJob : public PlaterJob
|
|||
arrangement::ArrangeParams params;
|
||||
|
||||
int m_status_range = 0;
|
||||
|
||||
protected:
|
||||
|
||||
void prepare() override;
|
||||
void process() override;
|
||||
Plater *m_plater;
|
||||
|
||||
public:
|
||||
FillBedJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
{}
|
||||
|
||||
int status_range() const override
|
||||
void prepare();
|
||||
void process(Ctl &ctl) override;
|
||||
|
||||
FillBedJob();
|
||||
|
||||
int status_range() const
|
||||
{
|
||||
return m_status_range;
|
||||
}
|
||||
|
||||
void finalize() override;
|
||||
void finalize(bool canceled, std::exception_ptr &e) override;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include <exception>
|
||||
|
||||
#include "Job.hpp"
|
||||
#include <libslic3r/Thread.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void GUI::Job::run(std::exception_ptr &eptr)
|
||||
{
|
||||
m_running.store(true);
|
||||
try {
|
||||
process();
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
m_running.store(false);
|
||||
|
||||
// ensure to call the last status to finalize the job
|
||||
update_status(status_range(), "");
|
||||
}
|
||||
|
||||
void GUI::Job::update_status(int st, const wxString &msg)
|
||||
{
|
||||
auto evt = new wxThreadEvent(wxEVT_THREAD, m_thread_evt_id);
|
||||
evt->SetInt(st);
|
||||
evt->SetString(msg);
|
||||
wxQueueEvent(this, evt);
|
||||
}
|
||||
|
||||
void GUI::Job::update_percent_finish()
|
||||
{
|
||||
m_progress->clear_percent();
|
||||
}
|
||||
|
||||
void GUI::Job::show_error_info(wxString msg, int code, wxString description, wxString extra)
|
||||
{
|
||||
m_progress->show_error_info(msg, code, description, extra);
|
||||
}
|
||||
|
||||
GUI::Job::Job(std::shared_ptr<ProgressIndicator> pri)
|
||||
: m_progress(std::move(pri))
|
||||
{
|
||||
m_thread_evt_id = wxNewId();
|
||||
|
||||
Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) {
|
||||
if (m_finalizing) return;
|
||||
|
||||
auto msg = evt.GetString();
|
||||
if (!msg.empty() && !m_worker_error)
|
||||
m_progress->set_status_text(msg.ToUTF8().data());
|
||||
|
||||
if (m_finalized) return;
|
||||
|
||||
m_progress->set_progress(evt.GetInt());
|
||||
if (evt.GetInt() == status_range() || m_worker_error) {
|
||||
// set back the original range and cancel callback
|
||||
m_progress->set_range(m_range);
|
||||
// Make sure progress indicators get the last value of their range
|
||||
// to make sure they close, fade out, whathever
|
||||
m_progress->set_progress(m_range);
|
||||
m_progress->set_cancel_callback();
|
||||
wxEndBusyCursor();
|
||||
|
||||
if (m_worker_error) {
|
||||
m_finalized = true;
|
||||
m_progress->set_status_text("");
|
||||
m_progress->set_progress(m_range);
|
||||
on_exception(m_worker_error);
|
||||
}
|
||||
else {
|
||||
// This is an RAII solution to remember that finalization is
|
||||
// running. The run method calls update_status(status_range(), "")
|
||||
// at the end, which queues up a call to this handler in all cases.
|
||||
// If process also calls update_status with maxed out status arg
|
||||
// it will call this handler twice. It is not a problem unless
|
||||
// yield is called inside the finilize() method, which would
|
||||
// jump out of finalize and call this handler again.
|
||||
struct Finalizing {
|
||||
bool &flag;
|
||||
Finalizing (bool &f): flag(f) { flag = true; }
|
||||
~Finalizing() { flag = false; }
|
||||
} fin(m_finalizing);
|
||||
|
||||
finalize();
|
||||
}
|
||||
|
||||
// dont do finalization again for the same process
|
||||
m_finalized = true;
|
||||
}
|
||||
}, m_thread_evt_id);
|
||||
}
|
||||
|
||||
void GUI::Job::start()
|
||||
{ // Start the job. No effect if the job is already running
|
||||
if (!m_running.load()) {
|
||||
prepare();
|
||||
|
||||
// Save the current status indicatior range and push the new one
|
||||
m_range = m_progress->get_range();
|
||||
m_progress->set_range(status_range());
|
||||
|
||||
// init cancellation flag and set the cancel callback
|
||||
m_canceled.store(false);
|
||||
m_progress->set_cancel_callback(
|
||||
[this]() { m_canceled.store(true); });
|
||||
|
||||
m_finalized = false;
|
||||
m_finalizing = false;
|
||||
|
||||
// Changing cursor to busy
|
||||
wxBeginBusyCursor();
|
||||
|
||||
try { // Execute the job
|
||||
m_worker_error = nullptr;
|
||||
m_thread = create_thread([this] { this->run(m_worker_error); });
|
||||
} catch (std::exception &) {
|
||||
update_status(status_range(),
|
||||
_(L("Error! Unable to create thread!")));
|
||||
}
|
||||
|
||||
// The state changes will be undone when the process hits the
|
||||
// last status value, in the status update handler (see ctor)
|
||||
}
|
||||
}
|
||||
|
||||
bool GUI::Job::join(int timeout_ms)
|
||||
{
|
||||
if (!m_thread.joinable()) return true;
|
||||
|
||||
if (timeout_ms <= 0)
|
||||
m_thread.join();
|
||||
else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GUI::ExclusiveJobGroup::start(size_t jid) {
|
||||
assert(jid < m_jobs.size());
|
||||
stop_all();
|
||||
m_jobs[jid]->start();
|
||||
}
|
||||
|
||||
void GUI::ExclusiveJobGroup::join_all(int wait_ms)
|
||||
{
|
||||
std::vector<bool> aborted(m_jobs.size(), false);
|
||||
|
||||
for (size_t jid = 0; jid < m_jobs.size(); ++jid)
|
||||
aborted[jid] = m_jobs[jid]->join(wait_ms);
|
||||
|
||||
if (!std::all_of(aborted.begin(), aborted.end(), [](bool t) { return t; }))
|
||||
BOOST_LOG_TRIVIAL(error) << "Could not abort a job!";
|
||||
}
|
||||
|
||||
bool GUI::ExclusiveJobGroup::is_any_running() const
|
||||
{
|
||||
return std::any_of(m_jobs.begin(), m_jobs.end(),
|
||||
[](const std::unique_ptr<GUI::Job> &j) {
|
||||
return j->is_running();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,130 +1,68 @@
|
|||
///|/ Copyright (c) Prusa Research 2019 - 2021 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef JOB_HPP
|
||||
#define JOB_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <slic3r/GUI/I18N.hpp>
|
||||
|
||||
#include "ProgressIndicator.hpp"
|
||||
|
||||
#include <wx/event.h>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// A class to handle UI jobs like arranging and optimizing rotation.
|
||||
// These are not instant jobs, the user has to be informed about their
|
||||
// state in the status progress indicator. On the other hand they are
|
||||
// separated from the background slicing process. Ideally, these jobs should
|
||||
// run when the background process is not running.
|
||||
//
|
||||
// TODO: A mechanism would be useful for blocking the plater interactions:
|
||||
// objects would be frozen for the user. In case of arrange, an animation
|
||||
// could be shown, or with the optimize orientations, partial results
|
||||
// could be displayed.
|
||||
class Job : public wxEvtHandler
|
||||
{
|
||||
int m_range = 100;
|
||||
int m_thread_evt_id = wxID_ANY;
|
||||
boost::thread m_thread;
|
||||
std::atomic<bool> m_running{false}, m_canceled{false};
|
||||
bool m_finalized = false, m_finalizing = false;
|
||||
std::shared_ptr<ProgressIndicator> m_progress;
|
||||
std::exception_ptr m_worker_error = nullptr;
|
||||
|
||||
void run(std::exception_ptr &);
|
||||
|
||||
protected:
|
||||
// status range for a particular job
|
||||
virtual int status_range() const { return 100; }
|
||||
|
||||
// status update, to be used from the work thread (process() method)
|
||||
void update_status(int st, const wxString &msg = "");
|
||||
|
||||
void update_percent_finish();
|
||||
|
||||
void show_error_info(wxString msg, int code, wxString description, wxString extra);
|
||||
|
||||
bool was_canceled() const { return m_canceled.load(); }
|
||||
|
||||
// Launched just before start(), a job can use it to prepare internals
|
||||
virtual void prepare() {}
|
||||
|
||||
// The method where the actual work of the job should be defined.
|
||||
virtual void process() = 0;
|
||||
|
||||
// Launched when the job is finished. It refreshes the 3Dscene by def.
|
||||
virtual void finalize() { m_finalized = true; }
|
||||
|
||||
// Exceptions occuring in process() are redirected from the worker thread
|
||||
// into the main (UI) thread. This method is called from the main thread and
|
||||
// can be overriden to handle these exceptions.
|
||||
virtual void on_exception(const std::exception_ptr &eptr)
|
||||
{
|
||||
if (eptr) std::rethrow_exception(eptr);
|
||||
}
|
||||
|
||||
// A class representing a job that is to be run in the background, not blocking
|
||||
// the main thread. Running it is up to a Worker object (see Worker interface)
|
||||
class Job {
|
||||
public:
|
||||
|
||||
enum JobPrepareState {
|
||||
PREPARE_STATE_DEFAULT = 0,
|
||||
PREPARE_STATE_MENU = 1,
|
||||
};
|
||||
|
||||
Job(std::shared_ptr<ProgressIndicator> pri);
|
||||
|
||||
bool is_finalized() const { return m_finalized; }
|
||||
|
||||
Job(const Job &) = delete;
|
||||
Job(Job &&) = delete;
|
||||
Job &operator=(const Job &) = delete;
|
||||
Job &operator=(Job &&) = delete;
|
||||
|
||||
void start();
|
||||
|
||||
// To wait for the running job and join the threads. False is
|
||||
// returned if the timeout has been reached and the job is still
|
||||
// running. Call cancel() before this fn if you want to explicitly
|
||||
// end the job.
|
||||
bool join(int timeout_ms = 0);
|
||||
|
||||
bool is_running() const { return m_running.load(); }
|
||||
void cancel() { m_canceled.store(true); }
|
||||
};
|
||||
// A controller interface that informs the job about cancellation and
|
||||
// makes it possible for the job to advertise its status.
|
||||
class Ctl {
|
||||
public:
|
||||
virtual ~Ctl() = default;
|
||||
|
||||
// Jobs defined inside the group class will be managed so that only one can
|
||||
// run at a time. Also, the background process will be stopped if a job is
|
||||
// started.
|
||||
class ExclusiveJobGroup
|
||||
{
|
||||
static const int ABORT_WAIT_MAX_MS = 10000;
|
||||
|
||||
std::vector<std::unique_ptr<GUI::Job>> m_jobs;
|
||||
|
||||
protected:
|
||||
virtual void before_start() {}
|
||||
|
||||
public:
|
||||
virtual ~ExclusiveJobGroup() = default;
|
||||
|
||||
size_t add_job(std::unique_ptr<GUI::Job> &&job)
|
||||
{
|
||||
m_jobs.emplace_back(std::move(job));
|
||||
return m_jobs.size() - 1;
|
||||
}
|
||||
|
||||
void start(size_t jid);
|
||||
|
||||
void cancel_all() { for (auto& j : m_jobs) j->cancel(); }
|
||||
|
||||
void join_all(int wait_ms = 0);
|
||||
|
||||
void stop_all() { cancel_all(); join_all(ABORT_WAIT_MAX_MS); }
|
||||
|
||||
bool is_any_running() const;
|
||||
// status update, to be used from the work thread (process() method)
|
||||
virtual void update_status(int st, const std::string &msg = "") = 0;
|
||||
|
||||
// Returns true if the job was asked to cancel itself.
|
||||
virtual bool was_canceled() const = 0;
|
||||
|
||||
// Orca:
|
||||
virtual void clear_percent() = 0;
|
||||
virtual void show_error_info(const std::string &msg, int code, const std::string &description, const std::string &extra) = 0;
|
||||
|
||||
// Execute a functor on the main thread. Note that the exact time of
|
||||
// execution is hard to determine. This can be used to make modifications
|
||||
// on the UI, like displaying some intermediate results or modify the
|
||||
// cursor.
|
||||
// This function returns a std::future<void> object which enables the
|
||||
// caller to optionally wait for the main thread to finish the function call.
|
||||
virtual std::future<void> call_on_main_thread(std::function<void()> fn) = 0;
|
||||
};
|
||||
|
||||
virtual ~Job() = default;
|
||||
|
||||
// The method where the actual work of the job should be defined. This is
|
||||
// run on the worker thread.
|
||||
virtual void process(Ctl &ctl) = 0;
|
||||
|
||||
// Launched when the job is finished on the UI thread.
|
||||
// If the job was cancelled, the first parameter will have a true value.
|
||||
// Exceptions occuring in process() are redirected from the worker thread
|
||||
// into the main (UI) thread. This method receives the exception and can
|
||||
// handle it properly. Assign nullptr to this second argument before
|
||||
// function return to prevent further action. Leaving it with a non-null
|
||||
// value will result in rethrowing by the worker.
|
||||
virtual void finalize(bool /*canceled*/, std::exception_ptr &) {}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "NotificationProgressIndicator.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
|
||||
|
@ -22,11 +26,15 @@ void NotificationProgressIndicator::set_range(int range)
|
|||
|
||||
void NotificationProgressIndicator::set_cancel_callback(CancelFn fn)
|
||||
{
|
||||
m_nm->progress_indicator_set_cancel_callback(std::move(fn));
|
||||
m_cancelfn = std::move(fn);
|
||||
m_nm->progress_indicator_set_cancel_callback(m_cancelfn);
|
||||
}
|
||||
|
||||
void NotificationProgressIndicator::set_progress(int pr)
|
||||
{
|
||||
if (!pr)
|
||||
set_cancel_callback(m_cancelfn);
|
||||
|
||||
m_nm->progress_indicator_set_progress(pr);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef NOTIFICATIONPROGRESSINDICATOR_HPP
|
||||
#define NOTIFICATIONPROGRESSINDICATOR_HPP
|
||||
|
||||
|
@ -9,6 +13,7 @@ class NotificationManager;
|
|||
|
||||
class NotificationProgressIndicator: public ProgressIndicator {
|
||||
NotificationManager *m_nm = nullptr;
|
||||
CancelFn m_cancelfn;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -140,29 +140,23 @@ void OrientJob::prepare()
|
|||
int state = m_plater->get_prepare_state();
|
||||
m_plater->get_notification_manager()->bbl_close_plateinfo_notification();
|
||||
if (state == Job::JobPrepareState::PREPARE_STATE_DEFAULT) {
|
||||
only_on_partplate = false;
|
||||
// only_on_partplate = false;
|
||||
prepare_selected();
|
||||
}
|
||||
else if (state == Job::JobPrepareState::PREPARE_STATE_MENU) {
|
||||
only_on_partplate = true; // only arrange items on current plate
|
||||
// only_on_partplate = true; // only arrange items on current plate
|
||||
prepare_partplate();
|
||||
}
|
||||
}
|
||||
|
||||
void OrientJob::on_exception(const std::exception_ptr &eptr)
|
||||
void OrientJob::process(Ctl &ctl)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &) {
|
||||
PlaterJob::on_exception(eptr);
|
||||
}
|
||||
}
|
||||
static const auto arrangestr = _u8L("Orienting...");
|
||||
|
||||
ctl.update_status(0, arrangestr);
|
||||
ctl.call_on_main_thread([this]{ prepare(); }).wait();;
|
||||
|
||||
void OrientJob::process()
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
static const auto arrangestr = _(L("Orienting..."));
|
||||
|
||||
const GLCanvas3D::OrientSettings& settings = m_plater->canvas3D()->get_orient_settings();
|
||||
|
||||
|
@ -177,11 +171,11 @@ void OrientJob::process()
|
|||
}
|
||||
|
||||
auto count = unsigned(m_selected.size() + m_unprintable.size());
|
||||
params.stopcondition = [this]() { return was_canceled(); };
|
||||
params.stopcondition = [&ctl]() { return ctl.was_canceled(); };
|
||||
|
||||
params.progressind = [this, count](unsigned st, std::string orientstr) {
|
||||
params.progressind = [this, count, &ctl](unsigned st, std::string orientstr) {
|
||||
st += m_unprintable.size();
|
||||
if (st > 0) update_status(int(st / float(count) * 100), _L("Orienting") + " " + orientstr);
|
||||
if (st > 0) ctl.update_status(int(st / float(count) * 100), _u8L("Orienting") + " " + orientstr);
|
||||
};
|
||||
|
||||
orientation::orient(m_selected, m_unselected, params);
|
||||
|
@ -194,15 +188,27 @@ void OrientJob::process()
|
|||
<< "Orientation: " << m_selected.back().orientation.transpose() << "; v,phi: " << m_selected.back().axis.transpose() << ", " << m_selected.back().angle << "; euler: " << m_selected.back().euler_angles.transpose();
|
||||
|
||||
// finalize just here.
|
||||
//update_status(int(count),
|
||||
// was_canceled() ? _(L("Orienting canceled."))
|
||||
// : _(L(ss.str().c_str())));
|
||||
wxGetApp().plater()->show_status_message(was_canceled() ? "Orienting canceled." : ss.str());
|
||||
ctl.update_status(100,
|
||||
ctl.was_canceled() ? _u8L("Orienting canceled.")
|
||||
: _u8L(ss.str().c_str()));
|
||||
wxGetApp().plater()->show_status_message(ctl.was_canceled() ? "Orienting canceled." : ss.str());
|
||||
}
|
||||
|
||||
void OrientJob::finalize() {
|
||||
OrientJob::OrientJob() : m_plater{wxGetApp().plater()} {}
|
||||
|
||||
void OrientJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
eptr = nullptr;
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
// Ignore the arrange result if aborted.
|
||||
if (was_canceled()) return;
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
|
||||
for (OrientMesh& mesh : m_selected)
|
||||
{
|
||||
|
@ -214,8 +220,6 @@ void OrientJob::finalize() {
|
|||
|
||||
// BBS
|
||||
//wxGetApp().obj_manipul()->set_dirty();
|
||||
|
||||
Job::finalize();
|
||||
}
|
||||
|
||||
orientation::OrientMesh OrientJob::get_orient_mesh(ModelInstance* instance)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef ORIENTJOB_HPP
|
||||
#define ORIENTJOB_HPP
|
||||
|
||||
#include "PlaterJob.hpp"
|
||||
#include "Job.hpp"
|
||||
#include "libslic3r/Orient.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -10,12 +10,15 @@ class ModelObject;
|
|||
|
||||
namespace GUI {
|
||||
|
||||
class OrientJob : public PlaterJob
|
||||
class Plater;
|
||||
|
||||
class OrientJob : public Job
|
||||
{
|
||||
using OrientMesh = orientation::OrientMesh;
|
||||
using OrientMeshs = orientation::OrientMeshs;
|
||||
|
||||
OrientMeshs m_selected, m_unselected, m_unprintable;
|
||||
Plater *m_plater;
|
||||
|
||||
// clear m_selected and m_unselected, reserve space for next usage
|
||||
void clear_input();
|
||||
|
@ -30,18 +33,14 @@ class OrientJob : public PlaterJob
|
|||
//BBS:prepare the items from current selected partplate
|
||||
void prepare_partplate();
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
|
||||
public:
|
||||
OrientJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
{}
|
||||
void prepare();
|
||||
|
||||
void process() override;
|
||||
void process(Ctl &ctl) override;
|
||||
|
||||
OrientJob();
|
||||
|
||||
void finalize() override;
|
||||
void finalize(bool canceled, std::exception_ptr &e) override;
|
||||
#if 0
|
||||
static
|
||||
orientation::OrientMesh get_orient_mesh(ModelObject* obj, const Plater* plater)
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#include "PlaterJob.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
void PlaterJob::on_exception(const std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &e) {
|
||||
show_error(m_plater, _(L("Exception")) + ": "+ e.what());
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef PLATERJOB_HPP
|
||||
#define PLATERJOB_HPP
|
||||
|
||||
#include "Job.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class Plater;
|
||||
|
||||
class PlaterJob : public Job {
|
||||
protected:
|
||||
Plater *m_plater;
|
||||
//BBS: add flag for whether on current part plate
|
||||
bool only_on_partplate{false};
|
||||
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
|
||||
public:
|
||||
|
||||
PlaterJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater):
|
||||
Job{std::move(pri)}, m_plater{plater} {}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // PLATERJOB_HPP
|
165
src/slic3r/GUI/Jobs/PlaterWorker.hpp
Normal file
165
src/slic3r/GUI/Jobs/PlaterWorker.hpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef PLATERWORKER_HPP
|
||||
#define PLATERWORKER_HPP
|
||||
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
|
||||
#include "Worker.hpp"
|
||||
#include "BusyCursorJob.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
template<class WorkerSubclass>
|
||||
class PlaterWorker: public Worker {
|
||||
WorkerSubclass m_w;
|
||||
wxWindow *m_plater;
|
||||
|
||||
class PlaterJob : public Job {
|
||||
std::unique_ptr<Job> m_job;
|
||||
wxWindow *m_plater;
|
||||
long long m_process_duration; // [ms]
|
||||
|
||||
public:
|
||||
void process(Ctl &c) override
|
||||
{
|
||||
// Ensure that wxWidgets processing wakes up to handle outgoing
|
||||
// messages in plater's wxIdle handler. Otherwise it might happen
|
||||
// that the message will only be processed when an event like mouse
|
||||
// move comes along which might be too late.
|
||||
struct WakeUpCtl: Ctl {
|
||||
Ctl &ctl;
|
||||
WakeUpCtl(Ctl &c) : ctl{c} {}
|
||||
|
||||
void update_status(int st, const std::string &msg = "") override
|
||||
{
|
||||
ctl.update_status(st, msg);
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
bool was_canceled() const override { return ctl.was_canceled(); }
|
||||
|
||||
std::future<void> call_on_main_thread(std::function<void()> fn) override
|
||||
{
|
||||
auto ftr = ctl.call_on_main_thread(std::move(fn));
|
||||
wxWakeUpIdle();
|
||||
|
||||
return ftr;
|
||||
}
|
||||
|
||||
void clear_percent() override {
|
||||
ctl.clear_percent();
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
void show_error_info(const std::string &msg, int code, const std::string &description, const std::string &extra) override
|
||||
{
|
||||
ctl.show_error_info(msg, code, description, extra);
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
} wctl{c};
|
||||
|
||||
CursorSetterRAII busycursor{wctl};
|
||||
|
||||
using namespace std::chrono;
|
||||
steady_clock::time_point process_start = steady_clock::now();
|
||||
m_job->process(wctl);
|
||||
steady_clock::time_point process_end = steady_clock::now();
|
||||
m_process_duration = duration_cast<milliseconds>(process_end - process_start).count();
|
||||
}
|
||||
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override
|
||||
{
|
||||
using namespace std::chrono;
|
||||
steady_clock::time_point finalize_start = steady_clock::now();
|
||||
m_job->finalize(canceled, eptr);
|
||||
steady_clock::time_point finalize_end = steady_clock::now();
|
||||
long long finalize_duration = duration_cast<milliseconds>(finalize_end - finalize_start).count();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< std::fixed // do not use scientific notations
|
||||
<< "Job '" << typeid(*m_job).name() << "' "
|
||||
<< "spend " << m_process_duration + finalize_duration << "ms "
|
||||
<< "(process " << m_process_duration << "ms + finalize " << finalize_duration << "ms)";
|
||||
|
||||
if (eptr) try {
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &e) {
|
||||
show_error(m_plater, _L("An unexpected error occured") + ": " + e.what());
|
||||
eptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
PlaterJob(wxWindow *p, std::unique_ptr<Job> j)
|
||||
: m_job{std::move(j)}, m_plater{p}
|
||||
{
|
||||
// TODO: decide if disabling slice button during UI job is what we
|
||||
// want.
|
||||
// if (m_plater)
|
||||
// m_plater->sidebar().enable_buttons(false);
|
||||
}
|
||||
|
||||
~PlaterJob() override
|
||||
{
|
||||
// TODO: decide if disabling slice button during UI job is what we want.
|
||||
|
||||
// Reload scene ensures that the slice button gets properly
|
||||
// enabled or disabled after the job finishes, depending on the
|
||||
// state of slicing. This might be an overkill but works for now.
|
||||
// if (m_plater)
|
||||
// m_plater->canvas3D()->reload_scene(false);
|
||||
}
|
||||
};
|
||||
|
||||
void on_idle(wxIdleEvent &evt)
|
||||
{
|
||||
process_events();
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template<class... WorkerArgs>
|
||||
PlaterWorker(wxWindow *plater, WorkerArgs &&...args)
|
||||
: m_w{std::forward<WorkerArgs>(args)...}, m_plater{plater}
|
||||
{
|
||||
// Ensure that messages from the worker thread to the UI thread are
|
||||
// processed continuously.
|
||||
plater->Bind(wxEVT_IDLE, &PlaterWorker::on_idle, this);
|
||||
}
|
||||
|
||||
~PlaterWorker()
|
||||
{
|
||||
m_plater->Unbind(wxEVT_IDLE, &PlaterWorker::on_idle, this);
|
||||
}
|
||||
|
||||
// Always package the job argument into a PlaterJob
|
||||
bool push(std::unique_ptr<Job> job) override
|
||||
{
|
||||
return m_w.push(std::make_unique<PlaterJob>(m_plater, std::move(job)));
|
||||
}
|
||||
|
||||
bool is_idle() const override { return m_w.is_idle(); }
|
||||
void cancel() override { m_w.cancel(); }
|
||||
void cancel_all() override { m_w.cancel_all(); }
|
||||
void process_events() override { m_w.process_events(); }
|
||||
bool wait_for_current_job(unsigned timeout_ms = 0) override
|
||||
{
|
||||
return m_w.wait_for_current_job(timeout_ms);
|
||||
}
|
||||
bool wait_for_idle(unsigned timeout_ms = 0) override
|
||||
{
|
||||
return m_w.wait_for_idle(timeout_ms);
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // PLATERJOB_HPP
|
|
@ -6,38 +6,39 @@
|
|||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "bambu_networking.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static wxString check_gcode_failed_str = _L("Abnormal print file data. Please slice again.");
|
||||
static wxString printjob_cancel_str = _L("Task canceled.");
|
||||
static wxString timeout_to_upload_str = _L("Upload task timed out. Please check the network status and try again.");
|
||||
static wxString failed_in_cloud_service_str = _L("Cloud service connection failed. Please try again.");
|
||||
static wxString file_is_not_exists_str = _L("Print file not found. please slice again.");
|
||||
static wxString file_over_size_str = _L("The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again.");
|
||||
static wxString print_canceled_str = _L("Task canceled.");
|
||||
static wxString send_print_failed_str = _L("Failed to send the print job. Please try again.");
|
||||
static wxString upload_ftp_failed_str = _L("Failed to upload file to ftp. Please try again.");
|
||||
static auto check_gcode_failed_str = _u8L("Abnormal print file data. Please slice again.");
|
||||
static auto printjob_cancel_str = _u8L("Task canceled.");
|
||||
static auto timeout_to_upload_str = _u8L("Upload task timed out. Please check the network status and try again.");
|
||||
static auto failed_in_cloud_service_str = _u8L("Cloud service connection failed. Please try again.");
|
||||
static auto file_is_not_exists_str = _u8L("Print file not found. please slice again.");
|
||||
static auto file_over_size_str = _u8L("The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again.");
|
||||
static auto print_canceled_str = _u8L("Task canceled.");
|
||||
static auto send_print_failed_str = _u8L("Failed to send the print job. Please try again.");
|
||||
static auto upload_ftp_failed_str = _u8L("Failed to upload file to ftp. Please try again.");
|
||||
|
||||
static wxString desc_network_error = _L("Check the current status of the bambu server by clicking on the link above.");
|
||||
static wxString desc_file_too_large = _L("The size of the print file is too large. Please adjust the file size and try again.");
|
||||
static wxString desc_fail_not_exist = _L("Print file not found, Please slice it again and send it for printing.");
|
||||
static wxString desc_upload_ftp_failed = _L("Failed to upload print file to FTP. Please check the network status and try again.");
|
||||
static auto desc_network_error = _u8L("Check the current status of the bambu server by clicking on the link above.");
|
||||
static auto desc_file_too_large = _u8L("The size of the print file is too large. Please adjust the file size and try again.");
|
||||
static auto desc_fail_not_exist = _u8L("Print file not found, Please slice it again and send it for printing.");
|
||||
static auto desc_upload_ftp_failed = _u8L("Failed to upload print file to FTP. Please check the network status and try again.");
|
||||
|
||||
static wxString sending_over_lan_str = _L("Sending print job over LAN");
|
||||
static wxString sending_over_cloud_str = _L("Sending print job through cloud service");
|
||||
static auto sending_over_lan_str = _u8L("Sending print job over LAN");
|
||||
static auto sending_over_cloud_str = _u8L("Sending print job through cloud service");
|
||||
|
||||
static wxString wait_sending_finish = _L("Print task sending times out.");
|
||||
static wxString desc_wait_sending_finish = _L("The printer timed out while receiving a print job. Please check if the network is functioning properly and send the print again.");
|
||||
static auto wait_sending_finish = _u8L("Print task sending times out.");
|
||||
static auto desc_wait_sending_finish = _u8L("The printer timed out while receiving a print job. Please check if the network is functioning properly and send the print again.");
|
||||
|
||||
PrintJob::PrintJob(std::shared_ptr<ProgressIndicator> pri, Plater* plater, std::string dev_id)
|
||||
: PlaterJob{ std::move(pri), plater },
|
||||
PrintJob::PrintJob(std::string dev_id)
|
||||
: m_plater{wxGetApp().plater()},
|
||||
m_dev_id(dev_id),
|
||||
m_is_calibration_task(false)
|
||||
{
|
||||
m_print_job_completed_id = plater->get_print_finished_event();
|
||||
m_print_job_completed_id = m_plater->get_print_finished_event();
|
||||
}
|
||||
|
||||
void PrintJob::prepare()
|
||||
|
@ -52,16 +53,6 @@ void PrintJob::prepare()
|
|||
}
|
||||
}
|
||||
|
||||
void PrintJob::on_exception(const std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &e) {
|
||||
PlaterJob::on_exception(eptr);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJob::on_success(std::function<void()> success)
|
||||
{
|
||||
m_success_fun = success;
|
||||
|
@ -131,22 +122,25 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body)
|
|||
return wxEmptyString;
|
||||
}
|
||||
|
||||
void PrintJob::process()
|
||||
void PrintJob::process(Ctl &ctl)
|
||||
{
|
||||
/* display info */
|
||||
wxString msg;
|
||||
std::string msg;
|
||||
wxString error_str;
|
||||
int curr_percent = 10;
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
AppConfig* config = wxGetApp().app_config;
|
||||
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending print job over LAN");
|
||||
msg = _u8L("Sending print job over LAN");
|
||||
}
|
||||
else {
|
||||
msg = _L("Sending print job through cloud service");
|
||||
msg = _u8L("Sending print job through cloud service");
|
||||
}
|
||||
|
||||
ctl.update_status(0, msg);
|
||||
ctl.call_on_main_thread([this] { prepare(); }).wait();
|
||||
|
||||
int result = -1;
|
||||
unsigned int http_code;
|
||||
std::string http_body;
|
||||
|
@ -162,12 +156,12 @@ void PrintJob::process()
|
|||
|
||||
/* check gcode is valid */
|
||||
if (!plate->is_valid_gcode_file() && m_print_type == "from_normal") {
|
||||
update_status(curr_percent, check_gcode_failed_str);
|
||||
ctl.update_status(curr_percent, check_gcode_failed_str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (was_canceled()) {
|
||||
update_status(curr_percent, printjob_cancel_str);
|
||||
if (ctl.was_canceled()) {
|
||||
ctl.update_status(curr_percent, printjob_cancel_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +293,7 @@ void PrintJob::process()
|
|||
}
|
||||
|
||||
wxString error_text;
|
||||
wxString msg_text;
|
||||
std::string msg_text;
|
||||
|
||||
|
||||
const int StagePercentPoint[(int)PrintingStageFinished + 1] = {
|
||||
|
@ -315,7 +309,7 @@ void PrintJob::process()
|
|||
bool is_try_lan_mode = false;
|
||||
bool is_try_lan_mode_failed = false;
|
||||
|
||||
auto update_fn = [this,
|
||||
auto update_fn = [this, &ctl,
|
||||
&is_try_lan_mode,
|
||||
&is_try_lan_mode_failed,
|
||||
&msg,
|
||||
|
@ -327,49 +321,49 @@ void PrintJob::process()
|
|||
|
||||
if (stage == BBL::SendingPrintJobStage::PrintingStageCreate && !is_try_lan_mode_failed) {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending print job over LAN");
|
||||
msg = _u8L("Sending print job over LAN");
|
||||
} else {
|
||||
msg = _L("Sending print job through cloud service");
|
||||
msg = _u8L("Sending print job through cloud service");
|
||||
}
|
||||
}
|
||||
else if (stage == BBL::SendingPrintJobStage::PrintingStageUpload && !is_try_lan_mode_failed) {
|
||||
if (code >= 0 && code <= 100 && !info.empty()) {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending print job over LAN");
|
||||
msg = _u8L("Sending print job over LAN");
|
||||
} else {
|
||||
msg = _L("Sending print job through cloud service");
|
||||
msg = _u8L("Sending print job through cloud service");
|
||||
}
|
||||
msg += wxString::Format("(%s)", info);
|
||||
msg += format("(%s)", info);
|
||||
}
|
||||
}
|
||||
else if (stage == BBL::SendingPrintJobStage::PrintingStageWaiting) {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending print job over LAN");
|
||||
msg = _u8L("Sending print job over LAN");
|
||||
} else {
|
||||
msg = _L("Sending print job through cloud service");
|
||||
msg = _u8L("Sending print job through cloud service");
|
||||
}
|
||||
}
|
||||
else if (stage == BBL::SendingPrintJobStage::PrintingStageRecord && !is_try_lan_mode) {
|
||||
msg = _L("Sending print configuration");
|
||||
msg = _u8L("Sending print configuration");
|
||||
}
|
||||
else if (stage == BBL::SendingPrintJobStage::PrintingStageSending && !is_try_lan_mode) {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending print job over LAN");
|
||||
msg = _u8L("Sending print job over LAN");
|
||||
} else {
|
||||
msg = _L("Sending print job through cloud service");
|
||||
msg = _u8L("Sending print job through cloud service");
|
||||
}
|
||||
}
|
||||
else if (stage == BBL::SendingPrintJobStage::PrintingStageFinished) {
|
||||
msg = wxString::Format(_L("Successfully sent. Will automatically jump to the device page in %ss"), info);
|
||||
msg = format(_u8L("Successfully sent. Will automatically jump to the device page in %ss"), info);
|
||||
if (m_print_job_completed_id == wxGetApp().plater()->get_send_calibration_finished_event()) {
|
||||
msg = wxString::Format(_L("Successfully sent. Will automatically jump to the next page in %ss"), info);
|
||||
msg = format(_u8L("Successfully sent. Will automatically jump to the next page in %ss"), info);
|
||||
}
|
||||
this->update_percent_finish();
|
||||
ctl.clear_percent();
|
||||
} else {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending print job over LAN");
|
||||
msg = _u8L("Sending print job over LAN");
|
||||
} else {
|
||||
msg = _L("Sending print job through cloud service");
|
||||
msg = _u8L("Sending print job through cloud service");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,22 +380,22 @@ void PrintJob::process()
|
|||
//get errors
|
||||
if (code > 100 || code < 0 || stage == BBL::SendingPrintJobStage::PrintingStageERROR) {
|
||||
if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_OVER_SIZE || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_OVER_SIZE) {
|
||||
m_plater->update_print_error_info(code, desc_file_too_large.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_file_too_large, info);
|
||||
}else if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST){
|
||||
m_plater->update_print_error_info(code, desc_fail_not_exist.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_fail_not_exist, info);
|
||||
}else if (code == BAMBU_NETWORK_ERR_PRINT_LP_UPLOAD_FTP_FAILED || code == BAMBU_NETWORK_ERR_PRINT_SG_UPLOAD_FTP_FAILED) {
|
||||
m_plater->update_print_error_info(code, desc_upload_ftp_failed.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_upload_ftp_failed, info);
|
||||
}else {
|
||||
m_plater->update_print_error_info(code, desc_network_error.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_network_error, info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->update_status(curr_percent, msg);
|
||||
ctl.update_status(curr_percent, msg);
|
||||
}
|
||||
};
|
||||
|
||||
auto cancel_fn = [this]() {
|
||||
return was_canceled();
|
||||
auto cancel_fn = [&ctl]() {
|
||||
return ctl.was_canceled();
|
||||
};
|
||||
|
||||
|
||||
|
@ -444,7 +438,7 @@ void PrintJob::process()
|
|||
boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
|
||||
}
|
||||
//this->update_status(curr_percent, _L("Print task sending times out."));
|
||||
m_plater->update_print_error_info(BAMBU_NETWORK_ERR_TIMEOUT, wait_sending_finish.ToStdString(), desc_wait_sending_finish.ToStdString());
|
||||
m_plater->update_print_error_info(BAMBU_NETWORK_ERR_TIMEOUT, wait_sending_finish, desc_wait_sending_finish);
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: timeout, cancel the job" << obj->job_id_;
|
||||
/* handle tiemout */
|
||||
obj->command_task_cancel(curr_job_id);
|
||||
|
@ -475,7 +469,7 @@ void PrintJob::process()
|
|||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: use ftp send print only";
|
||||
this->update_status(curr_percent, _L("Sending print job over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending print job over LAN"));
|
||||
is_try_lan_mode = true;
|
||||
result = m_agent->start_local_print_with_record(params, update_fn, cancel_fn, wait_fn);
|
||||
if (result < 0) {
|
||||
|
@ -492,7 +486,7 @@ void PrintJob::process()
|
|||
&& this->has_sdcard) {
|
||||
// try to send local with record
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: try to start local print with record";
|
||||
this->update_status(curr_percent, _L("Sending print job over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending print job over LAN"));
|
||||
result = m_agent->start_local_print_with_record(params, update_fn, cancel_fn, wait_fn);
|
||||
if (result == 0) {
|
||||
params.comments = "";
|
||||
|
@ -507,22 +501,22 @@ void PrintJob::process()
|
|||
is_try_lan_mode_failed = true;
|
||||
// try to send with cloud
|
||||
BOOST_LOG_TRIVIAL(warning) << "print_job: try to send with cloud";
|
||||
this->update_status(curr_percent, _L("Sending print job through cloud service"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending print job through cloud service"));
|
||||
result = m_agent->start_print(params, update_fn, cancel_fn, wait_fn);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: send with cloud";
|
||||
this->update_status(curr_percent, _L("Sending print job through cloud service"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending print job through cloud service"));
|
||||
result = m_agent->start_print(params, update_fn, cancel_fn, wait_fn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this->has_sdcard) {
|
||||
this->update_status(curr_percent, _L("Sending print job over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending print job over LAN"));
|
||||
result = m_agent->start_local_print(params, update_fn, cancel_fn);
|
||||
} else {
|
||||
this->update_status(curr_percent, _L("An SD card needs to be inserted before printing via LAN."));
|
||||
ctl.update_status(curr_percent, _u8L("An SD card needs to be inserted before printing via LAN."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -542,13 +536,13 @@ void PrintJob::process()
|
|||
msg_text = upload_ftp_failed_str;
|
||||
} else if (result == BAMBU_NETWORK_ERR_CANCELED) {
|
||||
msg_text = print_canceled_str;
|
||||
this->update_status(0, msg_text);
|
||||
ctl.update_status(0, msg_text);
|
||||
} else {
|
||||
msg_text = send_print_failed_str;
|
||||
}
|
||||
|
||||
if (result != BAMBU_NETWORK_ERR_CANCELED) {
|
||||
this->show_error_info(msg_text, 0, "", "");
|
||||
ctl.show_error_info(msg_text, 0, "", "");
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << "print_job: failed, result = " << result;
|
||||
|
@ -574,10 +568,17 @@ void PrintJob::process()
|
|||
}
|
||||
}
|
||||
|
||||
void PrintJob::finalize() {
|
||||
if (was_canceled()) return;
|
||||
void PrintJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
eptr = nullptr;
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
Job::finalize();
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
}
|
||||
|
||||
void PrintJob::set_project_name(std::string name)
|
||||
|
@ -601,10 +602,10 @@ void PrintJob::on_check_ip_address_success(std::function<void()> func)
|
|||
m_enter_ip_address_fun_success = func;
|
||||
}
|
||||
|
||||
void PrintJob::connect_to_local_mqtt()
|
||||
{
|
||||
this->update_status(0, wxEmptyString);
|
||||
}
|
||||
// void PrintJob::connect_to_local_mqtt()
|
||||
// {
|
||||
// this->update_status(0, wxEmptyString);
|
||||
// }
|
||||
|
||||
void PrintJob::set_calibration_task(bool is_calibration)
|
||||
{
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "PlaterJob.hpp"
|
||||
#include "Job.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class Plater;
|
||||
|
||||
#define PRINT_JOB_SENDING_TIMEOUT 25
|
||||
|
||||
class PrintPrepareData
|
||||
|
@ -34,7 +36,7 @@ public:
|
|||
BedType bed_type = BedType::btDefault;
|
||||
};
|
||||
|
||||
class PrintJob : public PlaterJob
|
||||
class PrintJob : public Job
|
||||
{
|
||||
std::function<void()> m_success_fun{nullptr};
|
||||
std::string m_dev_id;
|
||||
|
@ -43,16 +45,14 @@ class PrintJob : public PlaterJob
|
|||
wxString m_completed_evt_data;
|
||||
std::function<void()> m_enter_ip_address_fun_fail{ nullptr };
|
||||
std::function<void()> m_enter_ip_address_fun_success{ nullptr };
|
||||
Plater *m_plater;
|
||||
|
||||
public:
|
||||
PrintPrepareData job_data;
|
||||
PlateListData plate_data;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
public:
|
||||
PrintJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater, std::string dev_id = "");
|
||||
void prepare();
|
||||
PrintJob(std::string dev_id = "");
|
||||
|
||||
std::string m_project_name;
|
||||
std::string m_dev_ip;
|
||||
|
@ -90,7 +90,7 @@ public:
|
|||
task_layer_inspect = layer_inspect;
|
||||
}
|
||||
|
||||
int status_range() const override
|
||||
int status_range() const
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
@ -101,13 +101,13 @@ public:
|
|||
m_completed_evt_data = evt_data;
|
||||
}
|
||||
void on_success(std::function<void()> success);
|
||||
void process() override;
|
||||
void finalize() override;
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &e) override;
|
||||
void set_project_name(std::string name);
|
||||
void set_dst_name(std::string path);
|
||||
void on_check_ip_address_fail(std::function<void()> func);
|
||||
void on_check_ip_address_success(std::function<void()> func);
|
||||
void connect_to_local_mqtt();
|
||||
// void connect_to_local_mqtt();
|
||||
wxString get_http_error_msg(unsigned int status, std::string body);
|
||||
std::string truncate_string(const std::string& str, size_t maxLength);
|
||||
void set_calibration_task(bool is_calibration);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2020 Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef IPROGRESSINDICATOR_HPP
|
||||
#define IPROGRESSINDICATOR_HPP
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "RotoptimizeJob.hpp"
|
||||
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
|
@ -45,21 +49,24 @@ void RotoptimizeJob::prepare()
|
|||
}
|
||||
}
|
||||
|
||||
void RotoptimizeJob::process()
|
||||
void RotoptimizeJob::process(Ctl &ctl)
|
||||
{
|
||||
int prev_status = 0;
|
||||
auto statustxt = _u8L("Searching for optimal orientation");
|
||||
ctl.update_status(0, statustxt);
|
||||
|
||||
auto params =
|
||||
sla::RotOptimizeParams{}
|
||||
.accuracy(m_accuracy)
|
||||
.print_config(&m_default_print_cfg)
|
||||
.statucb([this, &prev_status](int s)
|
||||
.statucb([this, &prev_status, &ctl/*, &statustxt*/](int s)
|
||||
{
|
||||
if (s > 0 && s < 100)
|
||||
;
|
||||
// update_status(prev_status + s / m_selected_object_ids.size(),
|
||||
// _(L("Searching for optimal orientation...")));
|
||||
// ctl.update_status(prev_status + s / m_selected_object_ids.size(),
|
||||
// statustxt);
|
||||
|
||||
return !was_canceled();
|
||||
return !ctl.was_canceled();
|
||||
});
|
||||
|
||||
|
||||
|
@ -72,16 +79,19 @@ void RotoptimizeJob::process()
|
|||
|
||||
prev_status += 100 / m_selected_object_ids.size();
|
||||
|
||||
if (was_canceled()) break;
|
||||
if (ctl.was_canceled()) break;
|
||||
}
|
||||
|
||||
// update_status(100, was_canceled() ? _(L("Orientation search canceled.")) :
|
||||
// _(L("Orientation found.")));
|
||||
ctl.update_status(100, ctl.was_canceled() ? _u8L("Orientation search canceled.") :
|
||||
_u8L("Orientation found."));
|
||||
}
|
||||
|
||||
void RotoptimizeJob::finalize()
|
||||
RotoptimizeJob::RotoptimizeJob() : m_plater{wxGetApp().plater()} { prepare(); }
|
||||
|
||||
void RotoptimizeJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
if (was_canceled()) return;
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
|
||||
for (const ObjRot &objrot : m_selected_object_ids) {
|
||||
ModelObject *o = m_plater->model().objects[size_t(objrot.idx)];
|
||||
|
@ -112,10 +122,8 @@ void RotoptimizeJob::finalize()
|
|||
// m_plater->find_new_position(o->instances);
|
||||
}
|
||||
|
||||
if (!was_canceled())
|
||||
if (!canceled)
|
||||
m_plater->update();
|
||||
|
||||
Job::finalize();
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef ROTOPTIMIZEJOB_HPP
|
||||
#define ROTOPTIMIZEJOB_HPP
|
||||
|
||||
#include "PlaterJob.hpp"
|
||||
#include "Job.hpp"
|
||||
|
||||
#include "libslic3r/SLA/Rotfinder.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class RotoptimizeJob : public PlaterJob
|
||||
class Plater;
|
||||
|
||||
class RotoptimizeJob : public Job
|
||||
{
|
||||
using FindFn = std::function<Vec2d(const ModelObject & mo,
|
||||
const sla::RotOptimizeParams ¶ms)>;
|
||||
|
@ -44,19 +51,16 @@ class RotoptimizeJob : public PlaterJob
|
|||
};
|
||||
|
||||
std::vector<ObjRot> m_selected_object_ids;
|
||||
|
||||
protected:
|
||||
|
||||
void prepare() override;
|
||||
void process() override;
|
||||
Plater *m_plater;
|
||||
|
||||
public:
|
||||
|
||||
RotoptimizeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}
|
||||
{}
|
||||
void prepare();
|
||||
void process(Ctl &ctl) override;
|
||||
|
||||
void finalize() override;
|
||||
RotoptimizeJob();
|
||||
|
||||
void finalize(bool canceled, std::exception_ptr &) override;
|
||||
|
||||
static constexpr size_t get_methods_count() { return std::size(Methods); }
|
||||
|
||||
|
|
104
src/slic3r/GUI/Jobs/SLAImportDialog.hpp
Normal file
104
src/slic3r/GUI/Jobs/SLAImportDialog.hpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef SLAIMPORTDIALOG_HPP
|
||||
#define SLAIMPORTDIALOG_HPP
|
||||
|
||||
#include "SLAImportJob.hpp"
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/filepicker.h>
|
||||
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
// #include "libslic3r/Model.hpp"
|
||||
// #include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class SLAImportDialog : public wxDialog, public SLAImportJobView
|
||||
{
|
||||
wxFilePickerCtrl *m_filepicker;
|
||||
wxComboBox *m_import_dropdown, *m_quality_dropdown;
|
||||
|
||||
public:
|
||||
SLAImportDialog(Plater *plater) : wxDialog{plater, wxID_ANY, "Import SLA archive"}
|
||||
{
|
||||
auto szvert = new wxBoxSizer{wxVERTICAL};
|
||||
auto szfilepck = new wxBoxSizer{wxHORIZONTAL};
|
||||
|
||||
m_filepicker = new wxFilePickerCtrl(this, wxID_ANY, from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")),
|
||||
"SL1 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP",
|
||||
wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER);
|
||||
szfilepck->Add(m_filepicker, 1);
|
||||
szvert->Add(szfilepck, 0, wxALL | wxEXPAND, 5);
|
||||
|
||||
auto szchoices = new wxBoxSizer{wxHORIZONTAL};
|
||||
|
||||
static const std::vector<wxString> inp_choices = {_(L("Import model and profile")), _(L("Import profile only")),
|
||||
_(L("Import model only"))};
|
||||
|
||||
m_import_dropdown = new wxComboBox(this, wxID_ANY, inp_choices[0], wxDefaultPosition, wxDefaultSize, inp_choices.size(),
|
||||
inp_choices.data(), wxCB_READONLY | wxCB_DROPDOWN);
|
||||
|
||||
szchoices->Add(m_import_dropdown);
|
||||
szchoices->Add(new wxStaticText(this, wxID_ANY, _L("Quality") + ": "), 0, wxALIGN_CENTER | wxALL, 5);
|
||||
|
||||
static const std::vector<wxString> qual_choices = {_(L("Accurate")), _(L("Balanced")), _(L("Quick"))};
|
||||
|
||||
m_quality_dropdown = new wxComboBox(this, wxID_ANY, qual_choices[0], wxDefaultPosition, wxDefaultSize, qual_choices.size(),
|
||||
qual_choices.data(), wxCB_READONLY | wxCB_DROPDOWN);
|
||||
szchoices->Add(m_quality_dropdown);
|
||||
|
||||
m_import_dropdown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
|
||||
if (get_selection() == Sel::profileOnly)
|
||||
m_quality_dropdown->Disable();
|
||||
else
|
||||
m_quality_dropdown->Enable();
|
||||
});
|
||||
|
||||
szvert->Add(szchoices, 0, wxALL, 5);
|
||||
szvert->AddStretchSpacer(1);
|
||||
auto szbtn = new wxBoxSizer(wxHORIZONTAL);
|
||||
szbtn->Add(new wxButton{this, wxID_CANCEL});
|
||||
szbtn->Add(new wxButton{this, wxID_OK});
|
||||
szvert->Add(szbtn, 0, wxALIGN_RIGHT | wxALL, 5);
|
||||
|
||||
SetSizerAndFit(szvert);
|
||||
}
|
||||
|
||||
Sel get_selection() const override
|
||||
{
|
||||
int sel = m_import_dropdown->GetSelection();
|
||||
return Sel(std::min(int(Sel::modelOnly), std::max(0, sel)));
|
||||
}
|
||||
|
||||
Vec2i get_marchsq_windowsize() const override
|
||||
{
|
||||
enum { Accurate, Balanced, Fast };
|
||||
|
||||
switch (m_quality_dropdown->GetSelection()) {
|
||||
case Fast: return {8, 8};
|
||||
case Balanced: return {4, 4};
|
||||
default:
|
||||
case Accurate: return {2, 2};
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_path() const override { return m_filepicker->GetPath().ToUTF8().data(); }
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // SLAIMPORTDIALOG_HPP
|
|
@ -1,9 +1,12 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv, David Kocík @kocikdav
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "SLAImportJob.hpp"
|
||||
|
||||
#include "libslic3r/Format/SL1.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
|
@ -11,104 +14,10 @@
|
|||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/filepicker.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
enum class Sel { modelAndProfile, profileOnly, modelOnly};
|
||||
|
||||
class ImportDlg: public wxDialog {
|
||||
wxFilePickerCtrl *m_filepicker;
|
||||
wxComboBox *m_import_dropdown, *m_quality_dropdown;
|
||||
|
||||
public:
|
||||
ImportDlg(Plater *plater)
|
||||
: wxDialog{plater, wxID_ANY, "Import SLA archive"}
|
||||
{
|
||||
auto szvert = new wxBoxSizer{wxVERTICAL};
|
||||
auto szfilepck = new wxBoxSizer{wxHORIZONTAL};
|
||||
|
||||
m_filepicker = new wxFilePickerCtrl(this, wxID_ANY,
|
||||
from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")),
|
||||
"SL1 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP",
|
||||
wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER);
|
||||
szfilepck->Add(m_filepicker, 1);
|
||||
szvert->Add(szfilepck, 0, wxALL | wxEXPAND, 5);
|
||||
|
||||
auto szchoices = new wxBoxSizer{wxHORIZONTAL};
|
||||
|
||||
static const std::vector<wxString> inp_choices = {
|
||||
_(L("Import model and profile")),
|
||||
_(L("Import profile only")),
|
||||
_(L("Import model only"))
|
||||
};
|
||||
|
||||
m_import_dropdown = new wxComboBox(
|
||||
this, wxID_ANY, inp_choices[0], wxDefaultPosition, wxDefaultSize,
|
||||
inp_choices.size(), inp_choices.data(), wxCB_READONLY | wxCB_DROPDOWN);
|
||||
|
||||
szchoices->Add(m_import_dropdown);
|
||||
szchoices->Add(new wxStaticText(this, wxID_ANY, _L("Quality") + ": "), 0, wxALIGN_CENTER | wxALL, 5);
|
||||
|
||||
static const std::vector<wxString> qual_choices = {
|
||||
_(L("Accurate")),
|
||||
_(L("Balanced")),
|
||||
_(L("Quick"))
|
||||
};
|
||||
|
||||
m_quality_dropdown = new wxComboBox(
|
||||
this, wxID_ANY, qual_choices[0], wxDefaultPosition, wxDefaultSize,
|
||||
qual_choices.size(), qual_choices.data(), wxCB_READONLY | wxCB_DROPDOWN);
|
||||
szchoices->Add(m_quality_dropdown);
|
||||
|
||||
m_import_dropdown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
|
||||
if (get_selection() == Sel::profileOnly)
|
||||
m_quality_dropdown->Disable();
|
||||
else m_quality_dropdown->Enable();
|
||||
});
|
||||
|
||||
szvert->Add(szchoices, 0, wxALL, 5);
|
||||
szvert->AddStretchSpacer(1);
|
||||
auto szbtn = new wxBoxSizer(wxHORIZONTAL);
|
||||
szbtn->Add(new wxButton{this, wxID_CANCEL});
|
||||
szbtn->Add(new wxButton{this, wxID_OK});
|
||||
szvert->Add(szbtn, 0, wxALIGN_RIGHT | wxALL, 5);
|
||||
|
||||
SetSizerAndFit(szvert);
|
||||
}
|
||||
|
||||
Sel get_selection() const
|
||||
{
|
||||
int sel = m_import_dropdown->GetSelection();
|
||||
return Sel(std::min(int(Sel::modelOnly), std::max(0, sel)));
|
||||
}
|
||||
|
||||
Vec2i get_marchsq_windowsize() const
|
||||
{
|
||||
enum { Accurate, Balanced, Fast};
|
||||
|
||||
switch(m_quality_dropdown->GetSelection())
|
||||
{
|
||||
case Fast: return {8, 8};
|
||||
case Balanced: return {4, 4};
|
||||
default:
|
||||
case Accurate:
|
||||
return {2, 2};
|
||||
}
|
||||
}
|
||||
|
||||
wxString get_path() const
|
||||
{
|
||||
return m_filepicker->GetPath();
|
||||
}
|
||||
};
|
||||
|
||||
class SLAImportJob::priv {
|
||||
public:
|
||||
Plater *plater;
|
||||
|
@ -122,23 +31,28 @@ public:
|
|||
std::string err;
|
||||
ConfigSubstitutions config_substitutions;
|
||||
|
||||
ImportDlg import_dlg;
|
||||
const SLAImportJobView * import_dlg;
|
||||
|
||||
priv(Plater *plt) : plater{plt}, import_dlg{plt} {}
|
||||
priv(Plater *plt, const SLAImportJobView *view) : plater{plt}, import_dlg{view} {}
|
||||
};
|
||||
|
||||
SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
: PlaterJob{std::move(pri), plater}, p{std::make_unique<priv>(plater)}
|
||||
{}
|
||||
SLAImportJob::SLAImportJob(const SLAImportJobView *view)
|
||||
: p{std::make_unique<priv>(wxGetApp().plater(), view)}
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
SLAImportJob::~SLAImportJob() = default;
|
||||
|
||||
void SLAImportJob::process()
|
||||
void SLAImportJob::process(Ctl &ctl)
|
||||
{
|
||||
auto progr = [this](int s) {
|
||||
auto statustxt = _u8L("Importing SLA archive");
|
||||
ctl.update_status(0, statustxt);
|
||||
|
||||
auto progr = [&ctl, &statustxt](int s) {
|
||||
if (s < 100)
|
||||
update_status(int(s), _(L("Importing SLA archive")));
|
||||
return !was_canceled();
|
||||
ctl.update_status(int(s), statustxt);
|
||||
return !ctl.was_canceled();
|
||||
};
|
||||
|
||||
if (p->path.empty()) return;
|
||||
|
@ -161,15 +75,15 @@ void SLAImportJob::process()
|
|||
p->err = ex.what();
|
||||
}
|
||||
|
||||
update_status(100, was_canceled() ? _(L("Importing canceled.")) :
|
||||
_(L("Importing done.")));
|
||||
ctl.update_status(100, ctl.was_canceled() ? _u8L("Importing canceled.") :
|
||||
_u8L("Importing done."));
|
||||
}
|
||||
|
||||
void SLAImportJob::reset()
|
||||
{
|
||||
p->sel = Sel::modelAndProfile;
|
||||
p->mesh = {};
|
||||
p->profile = m_plater->sla_print().full_print_config();
|
||||
p->profile = p->plater->sla_print().full_print_config();
|
||||
p->win = {2, 2};
|
||||
p->path.Clear();
|
||||
}
|
||||
|
@ -178,22 +92,19 @@ void SLAImportJob::prepare()
|
|||
{
|
||||
reset();
|
||||
|
||||
if (p->import_dlg.ShowModal() == wxID_OK) {
|
||||
auto path = p->import_dlg.get_path();
|
||||
auto nm = wxFileName(path);
|
||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
||||
p->sel = p->import_dlg.get_selection();
|
||||
p->win = p->import_dlg.get_marchsq_windowsize();
|
||||
p->config_substitutions.clear();
|
||||
} else {
|
||||
p->path = "";
|
||||
}
|
||||
auto path = p->import_dlg->get_path();
|
||||
auto nm = wxFileName(path);
|
||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
||||
p->sel = p->import_dlg->get_selection();
|
||||
p->win = p->import_dlg->get_marchsq_windowsize();
|
||||
p->config_substitutions.clear();
|
||||
}
|
||||
|
||||
void SLAImportJob::finalize()
|
||||
void SLAImportJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
// Ignore the arrange result if aborted.
|
||||
if (was_canceled()) return;
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
|
||||
if (!p->err.empty()) {
|
||||
show_error(p->plater, p->err);
|
||||
|
@ -204,7 +115,7 @@ void SLAImportJob::finalize()
|
|||
std::string name = wxFileName(p->path).GetName().ToUTF8().data();
|
||||
|
||||
if (p->profile.empty()) {
|
||||
m_plater->get_notification_manager()->push_notification(
|
||||
p->plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||
_L("The imported SLA archive did not contain any presets. "
|
||||
|
@ -213,7 +124,7 @@ void SLAImportJob::finalize()
|
|||
|
||||
if (p->sel != Sel::modelOnly) {
|
||||
if (p->profile.empty())
|
||||
p->profile = m_plater->sla_print().full_print_config();
|
||||
p->profile = p->plater->sla_print().full_print_config();
|
||||
|
||||
const ModelObjectPtrs& objects = p->plater->model().objects;
|
||||
for (auto object : objects)
|
||||
|
|
|
@ -1,22 +1,40 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2022 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef SLAIMPORTJOB_HPP
|
||||
#define SLAIMPORTJOB_HPP
|
||||
|
||||
#include "PlaterJob.hpp"
|
||||
#include "Job.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class SLAImportJob : public PlaterJob {
|
||||
class SLAImportJobView
|
||||
{
|
||||
public:
|
||||
enum Sel { modelAndProfile, profileOnly, modelOnly };
|
||||
|
||||
virtual ~SLAImportJobView() = default;
|
||||
|
||||
virtual Sel get_selection() const = 0;
|
||||
virtual Vec2i get_marchsq_windowsize() const = 0;
|
||||
virtual std::string get_path() const = 0;
|
||||
};
|
||||
|
||||
class Plater;
|
||||
|
||||
class SLAImportJob : public Job {
|
||||
class priv;
|
||||
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void process() override;
|
||||
void finalize() override;
|
||||
using Sel = SLAImportJobView::Sel;
|
||||
|
||||
public:
|
||||
SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater);
|
||||
void prepare();
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &) override;
|
||||
|
||||
SLAImportJob(const SLAImportJobView *);
|
||||
~SLAImportJob();
|
||||
|
||||
void reset();
|
||||
|
|
|
@ -5,33 +5,34 @@
|
|||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static wxString check_gcode_failed_str = _L("Abnormal print file data. Please slice again.");
|
||||
static wxString printjob_cancel_str = _L("Task canceled.");
|
||||
static wxString timeout_to_upload_str = _L("Upload task timed out. Please check the network status and try again.");
|
||||
static wxString failed_in_cloud_service_str = _L("Cloud service connection failed. Please try again.");
|
||||
static wxString file_is_not_exists_str = _L("Print file not found. please slice again.");
|
||||
static wxString file_over_size_str = _L("The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again.");
|
||||
static wxString print_canceled_str = _L("Task canceled.");
|
||||
static wxString send_print_failed_str = _L("Failed to send the print job. Please try again.");
|
||||
static wxString upload_ftp_failed_str = _L("Failed to upload file to ftp. Please try again.");
|
||||
static auto check_gcode_failed_str = _u8L("Abnormal print file data. Please slice again.");
|
||||
static auto printjob_cancel_str = _u8L("Task canceled.");
|
||||
static auto timeout_to_upload_str = _u8L("Upload task timed out. Please check the network status and try again.");
|
||||
static auto failed_in_cloud_service_str = _u8L("Cloud service connection failed. Please try again.");
|
||||
static auto file_is_not_exists_str = _u8L("Print file not found. please slice again.");
|
||||
static auto file_over_size_str = _u8L("The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again.");
|
||||
static auto print_canceled_str = _u8L("Task canceled.");
|
||||
static auto send_print_failed_str = _u8L("Failed to send the print job. Please try again.");
|
||||
static auto upload_ftp_failed_str = _u8L("Failed to upload file to ftp. Please try again.");
|
||||
|
||||
static wxString desc_network_error = _L("Check the current status of the bambu server by clicking on the link above.");
|
||||
static wxString desc_file_too_large = _L("The size of the print file is too large. Please adjust the file size and try again.");
|
||||
static wxString desc_fail_not_exist = _L("Print file not found, Please slice it again and send it for printing.");
|
||||
static wxString desc_upload_ftp_failed = _L("Failed to upload print file to FTP. Please check the network status and try again.");
|
||||
static auto desc_network_error = _u8L("Check the current status of the bambu server by clicking on the link above.");
|
||||
static auto desc_file_too_large = _u8L("The size of the print file is too large. Please adjust the file size and try again.");
|
||||
static auto desc_fail_not_exist = _u8L("Print file not found, Please slice it again and send it for printing.");
|
||||
static auto desc_upload_ftp_failed = _u8L("Failed to upload print file to FTP. Please check the network status and try again.");
|
||||
|
||||
static wxString sending_over_lan_str = _L("Sending print job over LAN");
|
||||
static wxString sending_over_cloud_str = _L("Sending print job through cloud service");
|
||||
static auto sending_over_lan_str = _u8L("Sending print job over LAN");
|
||||
static auto sending_over_cloud_str = _u8L("Sending print job through cloud service");
|
||||
|
||||
SendJob::SendJob(std::shared_ptr<ProgressIndicator> pri, Plater* plater, std::string dev_id)
|
||||
: PlaterJob{ std::move(pri), plater },
|
||||
SendJob::SendJob(std::string dev_id)
|
||||
: m_plater{wxGetApp().plater()},
|
||||
m_dev_id(dev_id)
|
||||
{
|
||||
m_print_job_completed_id = plater->get_send_finished_event();
|
||||
m_print_job_completed_id = m_plater->get_send_finished_event();
|
||||
}
|
||||
|
||||
void SendJob::prepare()
|
||||
|
@ -45,16 +46,6 @@ void SendJob::prepare()
|
|||
}
|
||||
}
|
||||
|
||||
void SendJob::on_exception(const std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &e) {
|
||||
PlaterJob::on_exception(eptr);
|
||||
}
|
||||
}
|
||||
|
||||
wxString SendJob::get_http_error_msg(unsigned int status, std::string body)
|
||||
{
|
||||
int code = 0;
|
||||
|
@ -112,10 +103,10 @@ inline std::string get_transform_string(int bytes)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
void SendJob::process()
|
||||
void SendJob::process(Ctl &ctl)
|
||||
{
|
||||
BBL::PrintParams params;
|
||||
wxString msg;
|
||||
std::string msg;
|
||||
int curr_percent = 10;
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
AppConfig* config = wxGetApp().app_config;
|
||||
|
@ -154,13 +145,15 @@ void SendJob::process()
|
|||
|
||||
|
||||
/* display info */
|
||||
msg = _L("Sending gcode file over LAN");
|
||||
msg = _u8L("Sending gcode file over LAN");
|
||||
/* if (this->connection_type == "lan") {
|
||||
msg = _L("Sending gcode file over LAN");
|
||||
msg = _u8L("Sending gcode file over LAN");
|
||||
}
|
||||
else {
|
||||
msg = _L("Sending gcode file through cloud service");
|
||||
msg = _u8L("Sending gcode file through cloud service");
|
||||
}*/
|
||||
ctl.call_on_main_thread([this] { prepare(); }).wait();
|
||||
ctl.update_status(0, msg);
|
||||
|
||||
int total_plate_num = m_plater->get_partplate_list().get_plate_count();
|
||||
|
||||
|
@ -183,19 +176,19 @@ void SendJob::process()
|
|||
}
|
||||
if (plate == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "can not find plate with valid gcode file when sending to print, plate_index="<< job_data.plate_idx;
|
||||
update_status(curr_percent, check_gcode_failed_str);
|
||||
ctl.update_status(curr_percent, check_gcode_failed_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* check gcode is valid */
|
||||
if (!plate->is_valid_gcode_file()) {
|
||||
update_status(curr_percent, check_gcode_failed_str);
|
||||
ctl.update_status(curr_percent, check_gcode_failed_str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (was_canceled()) {
|
||||
update_status(curr_percent, printjob_cancel_str);
|
||||
if (ctl.was_canceled()) {
|
||||
ctl.update_status(curr_percent, printjob_cancel_str);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -223,7 +216,7 @@ void SendJob::process()
|
|||
params.use_ssl_for_ftp = m_local_use_ssl_for_ftp;
|
||||
params.use_ssl_for_mqtt = m_local_use_ssl_for_mqtt;
|
||||
wxString error_text;
|
||||
wxString msg_text;
|
||||
std::string msg_text;
|
||||
|
||||
const int StagePercentPoint[(int)PrintingStageFinished + 1] = {
|
||||
20, // PrintingStageCreate
|
||||
|
@ -235,36 +228,37 @@ void SendJob::process()
|
|||
100 // PrintingStageFinished
|
||||
};
|
||||
|
||||
auto update_fn = [this, &msg, &curr_percent, &error_text, StagePercentPoint](int stage, int code, std::string info) {
|
||||
auto update_fn = [this, &ctl,
|
||||
&msg, &curr_percent, &error_text, StagePercentPoint](int stage, int code, std::string info) {
|
||||
if (stage == SendingPrintJobStage::PrintingStageCreate) {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending gcode file over LAN");
|
||||
msg = _u8L("Sending gcode file over LAN");
|
||||
} else {
|
||||
msg = _L("Sending gcode file to sdcard");
|
||||
msg = _u8L("Sending gcode file to sdcard");
|
||||
}
|
||||
}
|
||||
else if (stage == SendingPrintJobStage::PrintingStageUpload) {
|
||||
if (code >= 0 && code <= 100 && !info.empty()) {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending gcode file over LAN");
|
||||
msg = _u8L("Sending gcode file over LAN");
|
||||
}
|
||||
else {
|
||||
msg = _L("Sending gcode file to sdcard");
|
||||
msg = _u8L("Sending gcode file to sdcard");
|
||||
}
|
||||
if (!info.empty()) {
|
||||
msg += wxString::Format("(%s)", info);
|
||||
msg += format("(%s)", info);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (stage == SendingPrintJobStage::PrintingStageFinished) {
|
||||
msg = wxString::Format(_L("Successfully sent. Close current page in %s s"), info);
|
||||
msg = format(_u8L("Successfully sent. Close current page in %s s"), info);
|
||||
}
|
||||
else {
|
||||
if (this->connection_type == "lan") {
|
||||
msg = _L("Sending gcode file over LAN");
|
||||
msg = _u8L("Sending gcode file over LAN");
|
||||
}
|
||||
else {
|
||||
msg = _L("Sending gcode file over LAN");
|
||||
msg = _u8L("Sending gcode file over LAN");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,25 +274,25 @@ void SendJob::process()
|
|||
//get errors
|
||||
if (code > 100 || code < 0 || stage == BBL::SendingPrintJobStage::PrintingStageERROR) {
|
||||
if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_OVER_SIZE || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_OVER_SIZE) {
|
||||
m_plater->update_print_error_info(code, desc_file_too_large.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_file_too_large, info);
|
||||
}
|
||||
else if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST) {
|
||||
m_plater->update_print_error_info(code, desc_fail_not_exist.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_fail_not_exist, info);
|
||||
}
|
||||
else if (code == BAMBU_NETWORK_ERR_PRINT_LP_UPLOAD_FTP_FAILED || code == BAMBU_NETWORK_ERR_PRINT_SG_UPLOAD_FTP_FAILED) {
|
||||
m_plater->update_print_error_info(code, desc_upload_ftp_failed.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_upload_ftp_failed, info);
|
||||
}
|
||||
else {
|
||||
m_plater->update_print_error_info(code, desc_network_error.ToStdString(), info);
|
||||
m_plater->update_print_error_info(code, desc_network_error, info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->update_status(curr_percent, msg);
|
||||
ctl.update_status(curr_percent, msg);
|
||||
}
|
||||
};
|
||||
|
||||
auto cancel_fn = [this]() {
|
||||
return was_canceled();
|
||||
auto cancel_fn = [&ctl]() {
|
||||
return ctl.was_canceled();
|
||||
};
|
||||
|
||||
|
||||
|
@ -317,7 +311,7 @@ void SendJob::process()
|
|||
&& this->has_sdcard) {
|
||||
// try to send local with record
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode to printer";
|
||||
this->update_status(curr_percent, _L("Sending gcode file over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending gcode file over LAN"));
|
||||
result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
|
||||
if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) {
|
||||
params.comments = "upload_failed";
|
||||
|
@ -327,24 +321,24 @@ void SendJob::process()
|
|||
if (result < 0) {
|
||||
// try to send with cloud
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode file to printer";
|
||||
this->update_status(curr_percent, _L("Sending gcode file over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending gcode file over LAN"));
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode file to printer";
|
||||
this->update_status(curr_percent, _L("Sending gcode file over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending gcode file over LAN"));
|
||||
}
|
||||
} else {
|
||||
if (this->has_sdcard) {
|
||||
this->update_status(curr_percent, _L("Sending gcode file over LAN"));
|
||||
ctl.update_status(curr_percent, _u8L("Sending gcode file over LAN"));
|
||||
result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
|
||||
} else {
|
||||
this->update_status(curr_percent, _L("An SD card needs to be inserted before sending to printer."));
|
||||
ctl.update_status(curr_percent, _u8L("An SD card needs to be inserted before sending to printer."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (was_canceled()) {
|
||||
update_status(curr_percent, printjob_cancel_str);
|
||||
if (ctl.was_canceled()) {
|
||||
ctl.update_status(curr_percent, printjob_cancel_str);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -374,7 +368,7 @@ void SendJob::process()
|
|||
}
|
||||
|
||||
if (result != BAMBU_NETWORK_ERR_CANCELED) {
|
||||
this->show_error_info(msg_text, 0, "", "");
|
||||
ctl.show_error_info(msg_text, 0, "", "");
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(error) << "send_job: failed, result = " << result;
|
||||
|
||||
|
@ -404,10 +398,18 @@ void SendJob::on_check_ip_address_success(std::function<void()> func)
|
|||
}
|
||||
|
||||
|
||||
void SendJob::finalize() {
|
||||
if (was_canceled()) return;
|
||||
void SendJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
eptr = nullptr;
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
Job::finalize();
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
}
|
||||
|
||||
void SendJob::set_project_name(std::string name)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "PlaterJob.hpp"
|
||||
#include "Job.hpp"
|
||||
#include "PrintJob.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
@ -11,10 +11,12 @@ namespace fs = boost::filesystem;
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class Plater;
|
||||
|
||||
typedef std::function<void(int status, int code, std::string msg)> OnUpdateStatusFn;
|
||||
typedef std::function<bool()> WasCancelledFn;
|
||||
|
||||
class SendJob : public PlaterJob
|
||||
class SendJob : public Job
|
||||
{
|
||||
PrintPrepareData job_data;
|
||||
std::string m_dev_id;
|
||||
|
@ -25,14 +27,11 @@ class SendJob : public PlaterJob
|
|||
std::function<void()> m_success_fun{nullptr};
|
||||
std::function<void(int)> m_enter_ip_address_fun_fail{nullptr};
|
||||
std::function<void()> m_enter_ip_address_fun_success{nullptr};
|
||||
Plater *m_plater;
|
||||
|
||||
protected:
|
||||
|
||||
void prepare() override;
|
||||
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
public:
|
||||
SendJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater, std::string dev_id = "");
|
||||
void prepare();
|
||||
SendJob(std::string dev_id = "");
|
||||
|
||||
std::string m_project_name;
|
||||
std::string m_dev_ip;
|
||||
|
@ -49,7 +48,7 @@ public:
|
|||
|
||||
wxWindow* m_parent{nullptr};
|
||||
|
||||
int status_range() const override
|
||||
int status_range() const
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
@ -58,11 +57,11 @@ public:
|
|||
void set_check_mode() {m_is_check_mode = true;};
|
||||
void check_and_continue() {m_check_and_continue = true;};
|
||||
bool is_finished() { return m_job_finished; }
|
||||
void process() override;
|
||||
void process(Ctl &ctl) override;
|
||||
void on_success(std::function<void()> success);
|
||||
void on_check_ip_address_fail(std::function<void(int)> func);
|
||||
void on_check_ip_address_success(std::function<void()> func);
|
||||
void finalize() override;
|
||||
void finalize(bool canceled, std::exception_ptr &) override;
|
||||
void set_project_name(std::string name);
|
||||
};
|
||||
|
||||
|
|
128
src/slic3r/GUI/Jobs/ThreadSafeQueue.hpp
Normal file
128
src/slic3r/GUI/Jobs/ThreadSafeQueue.hpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef THREADSAFEQUEUE_HPP
|
||||
#define THREADSAFEQUEUE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Helper structure for overloads of ThreadSafeQueueSPSC::consume_one()
|
||||
// to block if the queue is empty.
|
||||
struct BlockingWait
|
||||
{
|
||||
// Timeout to wait for the arrival of new element into the queue.
|
||||
unsigned timeout_ms = 0;
|
||||
|
||||
// An optional atomic flag to set true if an incoming element gets
|
||||
// consumed. The flag will be atomically set to true when popping the
|
||||
// front of the queue.
|
||||
std::atomic<bool> *pop_flag = nullptr;
|
||||
};
|
||||
|
||||
// A thread safe queue for one producer and one consumer.
|
||||
template<class T,
|
||||
template<class, class...> class Container = std::deque,
|
||||
class... ContainerArgs>
|
||||
class ThreadSafeQueueSPSC
|
||||
{
|
||||
std::queue<T, Container<T, ContainerArgs...>> m_queue;
|
||||
mutable std::mutex m_mutex;
|
||||
std::condition_variable m_cond_var;
|
||||
public:
|
||||
|
||||
// Consume one element, block if the queue is empty.
|
||||
template<class Fn> bool consume_one(const BlockingWait &blkw, Fn &&fn)
|
||||
{
|
||||
static_assert(!std::is_reference_v<T>, "");
|
||||
static_assert(std::is_default_constructible_v<T>, "");
|
||||
static_assert(std::is_move_assignable_v<T> || std::is_copy_assignable_v<T>, "");
|
||||
|
||||
T el;
|
||||
{
|
||||
std::unique_lock lk{m_mutex};
|
||||
|
||||
auto pred = [this]{ return !m_queue.empty(); };
|
||||
if (blkw.timeout_ms > 0) {
|
||||
auto timeout = std::chrono::milliseconds(blkw.timeout_ms);
|
||||
if (!m_cond_var.wait_for(lk, timeout, pred))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
m_cond_var.wait(lk, pred);
|
||||
|
||||
if constexpr (std::is_move_assignable_v<T>)
|
||||
el = std::move(m_queue.front());
|
||||
else
|
||||
el = m_queue.front();
|
||||
|
||||
m_queue.pop();
|
||||
|
||||
if (blkw.pop_flag)
|
||||
// The optional flag is set before the lock us unlocked.
|
||||
blkw.pop_flag->store(true);
|
||||
}
|
||||
|
||||
fn(el);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Consume one element, return true if consumed, false if queue was empty.
|
||||
template<class Fn> bool consume_one(Fn &&fn)
|
||||
{
|
||||
T el;
|
||||
{
|
||||
std::unique_lock lk{m_mutex};
|
||||
if (!m_queue.empty()) {
|
||||
if constexpr (std::is_move_assignable_v<T>)
|
||||
el = std::move(m_queue.front());
|
||||
else
|
||||
el = m_queue.front();
|
||||
|
||||
m_queue.pop();
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
fn(el);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Push element into the queue.
|
||||
template<class...TArgs> void push(TArgs&&...el)
|
||||
{
|
||||
std::lock_guard lk{m_mutex};
|
||||
m_queue.emplace(std::forward<TArgs>(el)...);
|
||||
m_cond_var.notify_one();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard lk{m_mutex};
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
std::lock_guard lk{m_mutex};
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard lk{m_mutex};
|
||||
while (!m_queue.empty())
|
||||
m_queue.pop();
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // THREADSAFEQUEUE_HPP
|
|
@ -13,39 +13,28 @@ wxDEFINE_EVENT(EVT_DOWNLOAD_NETWORK_FAILED, wxCommandEvent);
|
|||
wxDEFINE_EVENT(EVT_INSTALL_NETWORK_FAILED, wxCommandEvent);
|
||||
|
||||
|
||||
UpgradeNetworkJob::UpgradeNetworkJob(std::shared_ptr<ProgressIndicator> pri)
|
||||
: Job{std::move(pri)}
|
||||
UpgradeNetworkJob::UpgradeNetworkJob()
|
||||
{
|
||||
name = "plugins";
|
||||
package_name = "networking_plugins.zip";
|
||||
}
|
||||
|
||||
void UpgradeNetworkJob::on_exception(const std::exception_ptr &eptr)
|
||||
{
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (std::exception &e) {
|
||||
UpgradeNetworkJob::on_exception(eptr);
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradeNetworkJob::on_success(std::function<void()> success)
|
||||
{
|
||||
m_success_fun = success;
|
||||
}
|
||||
|
||||
void UpgradeNetworkJob::update_status(int st, const wxString &msg)
|
||||
void UpgradeNetworkJob::update_status(Ctl &ctl, int st, const std::string &msg)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "UpgradeNetworkJob: percent = " << st << "msg = " << msg;
|
||||
GUI::Job::update_status(st, msg);
|
||||
ctl.update_status(st, msg);
|
||||
wxCommandEvent event(EVT_UPGRADE_UPDATE_MESSAGE);
|
||||
event.SetString(msg);
|
||||
event.SetEventObject(m_event_handle);
|
||||
wxPostEvent(m_event_handle, event);
|
||||
}
|
||||
|
||||
void UpgradeNetworkJob::process()
|
||||
void UpgradeNetworkJob::process(Ctl &ctl)
|
||||
{
|
||||
// downloading
|
||||
int result = 0;
|
||||
|
@ -64,24 +53,24 @@ void UpgradeNetworkJob::process()
|
|||
|
||||
BOOST_LOG_TRIVIAL(info) << "UpgradeNetworkJob: save netowrk_plugin to " << tmp_path.string();
|
||||
|
||||
auto cancel_fn = [this]() {
|
||||
return was_canceled();
|
||||
auto cancel_fn = [&ctl]() {
|
||||
return ctl.was_canceled();
|
||||
};
|
||||
int curr_percent = 0;
|
||||
result = wxGetApp().download_plugin(name, package_name,
|
||||
[this, &curr_percent](int state, int percent, bool &cancel) {
|
||||
[this, &ctl, &curr_percent](int state, int percent, bool &cancel) {
|
||||
if (state == InstallStatusNormal) {
|
||||
update_status(percent, _L("Downloading"));
|
||||
update_status(ctl, percent, _u8L("Downloading"));
|
||||
} else if (state == InstallStatusDownloadFailed) {
|
||||
update_status(percent, _L("Download failed"));
|
||||
update_status(ctl, percent, _u8L("Download failed"));
|
||||
} else {
|
||||
update_status(percent, _L("Downloading"));
|
||||
update_status(ctl, percent, _u8L("Downloading"));
|
||||
}
|
||||
curr_percent = percent;
|
||||
}, cancel_fn);
|
||||
|
||||
if (was_canceled()) {
|
||||
update_status(0, _L("Cancelled"));
|
||||
if (ctl.was_canceled()) {
|
||||
update_status(ctl, 0, _u8L("Cancelled"));
|
||||
wxCommandEvent event(wxEVT_CLOSE_WINDOW);
|
||||
event.SetEventObject(m_event_handle);
|
||||
wxPostEvent(m_event_handle, event);
|
||||
|
@ -89,7 +78,7 @@ void UpgradeNetworkJob::process()
|
|||
}
|
||||
|
||||
if (result < 0) {
|
||||
update_status(0, _L("Download failed"));
|
||||
update_status(ctl, 0, _u8L("Download failed"));
|
||||
wxCommandEvent event(EVT_DOWNLOAD_NETWORK_FAILED);
|
||||
event.SetEventObject(m_event_handle);
|
||||
wxPostEvent(m_event_handle, event);
|
||||
|
@ -98,16 +87,16 @@ void UpgradeNetworkJob::process()
|
|||
|
||||
result = wxGetApp().install_plugin(
|
||||
name, package_name,
|
||||
[this](int state, int percent, bool &cancel) {
|
||||
[this, &ctl](int state, int percent, bool &cancel) {
|
||||
if (state == InstallStatusInstallCompleted) {
|
||||
update_status(percent, _L("Install successfully."));
|
||||
update_status(ctl, percent, _u8L("Install successfully."));
|
||||
} else {
|
||||
update_status(percent, _L("Installing"));
|
||||
update_status(ctl, percent, _u8L("Installing"));
|
||||
}
|
||||
}, cancel_fn);
|
||||
|
||||
if (was_canceled()) {
|
||||
update_status(0, _L("Cancelled"));
|
||||
if (ctl.was_canceled()) {
|
||||
update_status(ctl, 0, _u8L("Cancelled"));
|
||||
wxCommandEvent event(wxEVT_CLOSE_WINDOW);
|
||||
event.SetEventObject(m_event_handle);
|
||||
wxPostEvent(m_event_handle, event);
|
||||
|
@ -115,7 +104,7 @@ void UpgradeNetworkJob::process()
|
|||
}
|
||||
|
||||
if (result != 0) {
|
||||
update_status(0, _L("Install failed"));
|
||||
update_status(ctl, 0, _u8L("Install failed"));
|
||||
wxCommandEvent event(EVT_INSTALL_NETWORK_FAILED);
|
||||
event.SetEventObject(m_event_handle);
|
||||
wxPostEvent(m_event_handle, event);
|
||||
|
@ -129,11 +118,18 @@ void UpgradeNetworkJob::process()
|
|||
return;
|
||||
}
|
||||
|
||||
void UpgradeNetworkJob::finalize()
|
||||
void UpgradeNetworkJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
{
|
||||
if (was_canceled()) return;
|
||||
try {
|
||||
if (eptr)
|
||||
std::rethrow_exception(eptr);
|
||||
eptr = nullptr;
|
||||
} catch (...) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
Job::finalize();
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
}
|
||||
|
||||
void UpgradeNetworkJob::set_event_handle(wxWindow *hanle)
|
||||
|
|
|
@ -32,11 +32,10 @@ protected:
|
|||
std::string name;
|
||||
std::string package_name;
|
||||
|
||||
void on_exception(const std::exception_ptr &) override;
|
||||
public:
|
||||
UpgradeNetworkJob(std::shared_ptr<ProgressIndicator> pri);
|
||||
UpgradeNetworkJob();
|
||||
|
||||
int status_range() const override
|
||||
int status_range() const
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
@ -44,9 +43,9 @@ public:
|
|||
bool is_finished() { return m_job_finished; }
|
||||
|
||||
void on_success(std::function<void()> success);
|
||||
void update_status(int st, const wxString &msg);
|
||||
void process() override;
|
||||
void finalize() override;
|
||||
void update_status(Ctl &ctl, int st, const std::string &msg);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &e) override;
|
||||
void set_event_handle(wxWindow* hanle);
|
||||
};
|
||||
|
||||
|
|
123
src/slic3r/GUI/Jobs/Worker.hpp
Normal file
123
src/slic3r/GUI/Jobs/Worker.hpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef PRUSALSICER_WORKER_HPP
|
||||
#define PRUSALSICER_WORKER_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Job.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// An interface of a worker that runs jobs on a dedicated worker thread, one
|
||||
// after the other. It is assumed that every method of this class is called
|
||||
// from the same main thread.
|
||||
class Worker {
|
||||
public:
|
||||
// Queue up a new job after the current one. This call does not block.
|
||||
// Returns false if the job gets discarded.
|
||||
virtual bool push(std::unique_ptr<Job> job) = 0;
|
||||
|
||||
// Returns true if no job is running, the job queue is empty and no job
|
||||
// message is left to be processed. This means that nothing is left to
|
||||
// finalize or take care of in the main thread.
|
||||
virtual bool is_idle() const = 0;
|
||||
|
||||
// Ask the current job gracefully to cancel. This call is not blocking and
|
||||
// the job may or may not cancel eventually, depending on its
|
||||
// implementation. Note that it is not trivial to kill a thread forcefully
|
||||
// and we don't need that.
|
||||
virtual void cancel() = 0;
|
||||
|
||||
// This method will delete the queued jobs and cancel the current one.
|
||||
virtual void cancel_all() = 0;
|
||||
|
||||
// Needs to be called continuously to process events (like status update
|
||||
// or finalizing of jobs) in the main thread. This can be done e.g. in a
|
||||
// wxIdle handler.
|
||||
virtual void process_events() = 0;
|
||||
|
||||
// Wait until the current job finishes. Timeout will only be considered
|
||||
// if not zero. Returns false if timeout is reached but the job has not
|
||||
// finished.
|
||||
virtual bool wait_for_current_job(unsigned timeout_ms = 0) = 0;
|
||||
|
||||
// Wait until the whole job queue finishes. Timeout will only be considered
|
||||
// if not zero. Returns false only if timeout is reached but the worker has
|
||||
// not reached the idle state.
|
||||
virtual bool wait_for_idle(unsigned timeout_ms = 0) = 0;
|
||||
|
||||
// The destructor shall properly close the worker thread.
|
||||
virtual ~Worker() = default;
|
||||
};
|
||||
|
||||
template<class Fn> constexpr bool IsProcessFn = std::is_invocable_v<Fn, Job::Ctl&>;
|
||||
template<class Fn> constexpr bool IsFinishFn = std::is_invocable_v<Fn, bool, std::exception_ptr&>;
|
||||
|
||||
// Helper function to use the worker with arbitrary functors.
|
||||
template<class ProcessFn, class FinishFn,
|
||||
class = std::enable_if_t<IsProcessFn<ProcessFn>>,
|
||||
class = std::enable_if_t<IsFinishFn<FinishFn>> >
|
||||
bool queue_job(Worker &w, ProcessFn fn, FinishFn finishfn)
|
||||
{
|
||||
struct LambdaJob: Job {
|
||||
ProcessFn fn;
|
||||
FinishFn finishfn;
|
||||
|
||||
LambdaJob(ProcessFn pfn, FinishFn ffn)
|
||||
: fn{std::move(pfn)}, finishfn{std::move(ffn)}
|
||||
{}
|
||||
|
||||
void process(Ctl &ctl) override { fn(ctl); }
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override
|
||||
{
|
||||
finishfn(canceled, eptr);
|
||||
}
|
||||
};
|
||||
|
||||
auto j = std::make_unique<LambdaJob>(std::move(fn), std::move(finishfn));
|
||||
return w.push(std::move(j));
|
||||
}
|
||||
|
||||
template<class ProcessFn, class = std::enable_if_t<IsProcessFn<ProcessFn>>>
|
||||
bool queue_job(Worker &w, ProcessFn fn)
|
||||
{
|
||||
return queue_job(w, std::move(fn), [](bool, std::exception_ptr &) {});
|
||||
}
|
||||
|
||||
inline bool queue_job(Worker &w, std::unique_ptr<Job> j)
|
||||
{
|
||||
return w.push(std::move(j));
|
||||
}
|
||||
|
||||
// Replace the current job queue with a new job. The signature is the same
|
||||
// as for queue_job(). This cancels all jobs and
|
||||
// will not wait. The new job will begin after the queue cancels properly.
|
||||
// Note that this can be called from the UI thread and will not block it if
|
||||
// the jobs take longer to cancel.
|
||||
template<class...Args> bool replace_job(Worker &w, Args&& ...args)
|
||||
{
|
||||
w.cancel_all();
|
||||
return queue_job(w, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Cancel the current job and wait for it to actually be stopped.
|
||||
inline bool stop_current_job(Worker &w, unsigned timeout_ms = 0)
|
||||
{
|
||||
w.cancel();
|
||||
return w.wait_for_current_job(timeout_ms);
|
||||
}
|
||||
|
||||
// Cancel all pending jobs including current one and wait until the worker
|
||||
// becomes idle.
|
||||
inline bool stop_queue(Worker &w, unsigned timeout_ms = 0)
|
||||
{
|
||||
w.cancel_all();
|
||||
return w.wait_for_idle(timeout_ms);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // WORKER_HPP
|
|
@ -1,3 +1,14 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, David Kocík @kocikdav, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
|
||||
///|/ Copyright (c) 2021 Jason Scurtu @xarbit
|
||||
///|/ Copyright (c) 2019 John Drake @foxox
|
||||
///|/
|
||||
///|/ ported from lib/Slic3r/GUI/MainFrame.pm:
|
||||
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966
|
||||
///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel
|
||||
///|/ Copyright (c) 2014 Mark Hindess
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "MainFrame.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
|
@ -811,7 +822,7 @@ void MainFrame::shutdown()
|
|||
#endif // _WIN32
|
||||
|
||||
if (m_plater != nullptr) {
|
||||
m_plater->stop_jobs();
|
||||
m_plater->get_ui_job_worker().cancel_all();
|
||||
|
||||
// Unbinding of wxWidgets event handling in canvases needs to be done here because on MAC,
|
||||
// when closing the application using Command+Q, a mouse event is triggered after this lambda is completed,
|
||||
|
|
|
@ -357,13 +357,13 @@ void MediaPlayCtrl::ToggleStream()
|
|||
DownloadProgressDialog2(MediaPlayCtrl *ctrl) : DownloadProgressDialog(_L("Downloading Virtual Camera Tools")), ctrl(ctrl) {}
|
||||
struct UpgradeNetworkJob2 : UpgradeNetworkJob
|
||||
{
|
||||
UpgradeNetworkJob2(std::shared_ptr<ProgressIndicator> pri) : UpgradeNetworkJob(pri) {
|
||||
UpgradeNetworkJob2() {
|
||||
name = "cameratools";
|
||||
package_name = "camera_tools.zip";
|
||||
}
|
||||
};
|
||||
std::shared_ptr<UpgradeNetworkJob> make_job(std::shared_ptr<ProgressIndicator> pri) override
|
||||
{ return std::make_shared<UpgradeNetworkJob2>(pri); }
|
||||
std::unique_ptr<UpgradeNetworkJob> make_job() override
|
||||
{ return std::make_unique<UpgradeNetworkJob2>(); }
|
||||
void on_finish() override
|
||||
{
|
||||
ctrl->CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); });
|
||||
|
|
|
@ -489,7 +489,7 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
{
|
||||
std::vector<unsigned> out;
|
||||
|
||||
const Transform3d& instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true);
|
||||
const Transform3d instance_matrix_no_translation_no_scaling = trafo.get_rotation_matrix();
|
||||
Vec3d direction_to_camera = -camera.get_dir_forward();
|
||||
Vec3d direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera).normalized().eval();
|
||||
direction_to_camera_mesh = direction_to_camera_mesh.cwiseProduct(trafo.get_scaling_factor());
|
||||
|
|
|
@ -147,8 +147,6 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n,
|
|||
, m_evt_handler (evt_handler)
|
||||
, m_notification_start (GLCanvas3D::timestamp_now())
|
||||
{
|
||||
m_is_dark = wxGetApp().plater()->get_current_canvas3D()->get_dark_mode_status();
|
||||
|
||||
m_ErrorColor = ImVec4(0.9, 0.36, 0.36, 1);
|
||||
m_WarnColor = ImVec4(0.99, 0.69, 0.455, 1);
|
||||
m_NormalColor = ImVec4(0, 0.588, 0.533, 1);
|
||||
|
@ -158,17 +156,32 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n,
|
|||
m_WindowBkgColor = ImVec4(1, 1, 1, 1);
|
||||
m_TextColor = ImVec4(.2f, .2f, .2f, 1.0f);
|
||||
m_HyperTextColor = ImVec4(0, 0.588, 0.533, 1);
|
||||
}
|
||||
|
||||
m_WindowRadius = 4.0f * wxGetApp().plater()->get_current_canvas3D()->get_scale();
|
||||
// We cannot call plater()->get_current_canvas3D() from constructor, so we do it here
|
||||
void NotificationManager::PopNotification::ensure_ui_inited()
|
||||
{
|
||||
if (!m_is_dark_inited) {
|
||||
m_is_dark = wxGetApp().plater()->get_current_canvas3D()->get_dark_mode_status();
|
||||
m_is_dark_inited = true;
|
||||
}
|
||||
|
||||
if (!m_WindowRadius_inited) {
|
||||
m_WindowRadius = 4.0f * wxGetApp().plater()->get_current_canvas3D()->get_scale();
|
||||
m_WindowRadius_inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::on_change_color_mode(bool is_dark)
|
||||
{
|
||||
m_is_dark_inited = true;
|
||||
m_is_dark = is_dark;
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::use_bbl_theme()
|
||||
{
|
||||
ensure_ui_inited();
|
||||
|
||||
ImGuiStyle &OldStyle = ImGui::GetStyle();
|
||||
|
||||
m_DefaultTheme.mWindowPadding = OldStyle.WindowPadding;
|
||||
|
@ -731,6 +744,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
|||
|
||||
void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ensure_ui_inited();
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
@ -856,6 +870,7 @@ void NotificationManager::PopNotification::bbl_render_block_notif_left_sign(ImGu
|
|||
|
||||
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)
|
||||
{
|
||||
ensure_ui_inited();
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 round_rect_pos = ImVec2(win_pos_x - win_size_x + ImGui::GetStyle().WindowBorderSize, win_pos_y + ImGui::GetStyle().WindowBorderSize);
|
||||
ImVec2 round_rect_size = ImVec2(m_WindowRadius * 2, win_size_y - 2 * ImGui::GetStyle().WindowBorderSize);
|
||||
|
@ -887,6 +902,7 @@ void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
|
|||
}
|
||||
void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ensure_ui_inited();
|
||||
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);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Hejl @hejllukas, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_NotificationManager_hpp_
|
||||
#define slic3r_GUI_NotificationManager_hpp_
|
||||
|
||||
|
@ -119,6 +123,8 @@ enum class NotificationType
|
|||
// Give user advice to simplify object with big amount of triangles
|
||||
// Contains ObjectID for closing when object is deleted
|
||||
SimplifySuggestion,
|
||||
// Change of text will change font to similar one on.
|
||||
UnknownFont,
|
||||
// information about netfabb is finished repairing model (blocking proccess)
|
||||
NetfabbFinished,
|
||||
// Short meesage to fill space between start and finish of export
|
||||
|
@ -462,7 +468,10 @@ private:
|
|||
// used this function instead of reading directly m_data.duration. Some notifications might need to return changing value.
|
||||
virtual int get_duration() { return m_data.duration; }
|
||||
|
||||
void ensure_ui_inited();
|
||||
|
||||
bool m_is_dark = false;
|
||||
bool m_is_dark_inited = false;
|
||||
|
||||
const NotificationData m_data;
|
||||
// For reusing ImGUI windows.
|
||||
|
@ -492,6 +501,7 @@ private:
|
|||
ImVec4 m_CurrentColor;
|
||||
|
||||
float m_WindowRadius;
|
||||
bool m_WindowRadius_inited = false;
|
||||
|
||||
void use_bbl_theme();
|
||||
void restore_default_theme();
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral
|
||||
///|/ Copyright (c) 2020 Gianni Ceccarelli @dakkar
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "ObjectDataViewModel.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
@ -12,12 +17,23 @@
|
|||
#include <wx/bmpcbox.h>
|
||||
#include <wx/dc.h>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
|
||||
|
||||
BitmapCache* m_bitmap_cache = nullptr;
|
||||
|
||||
wxBitmapBundle* find_bndl(const std::string& bmp_name)
|
||||
{
|
||||
if (!m_bitmap_cache)
|
||||
m_bitmap_cache = new BitmapCache;
|
||||
|
||||
return m_bitmap_cache->find_bndl(bmp_name);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// ----------------------------------------------------------------------------
|
||||
// ObjectDataViewModelNode
|
||||
|
@ -72,19 +88,19 @@ const std::map<InfoItemType, InfoItemAtributes> INFO_ITEMS{
|
|||
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||
const wxString& sub_obj_name,
|
||||
Slic3r::ModelVolumeType type,
|
||||
const wxBitmapBundle& bmp,
|
||||
const bool is_text_volume,
|
||||
const bool is_svg_volume,
|
||||
const wxString& extruder,
|
||||
const int idx/* = -1*/,
|
||||
const std::string& warning_icon_name /*= std::string*/) :
|
||||
const int idx/* = -1*/) :
|
||||
m_parent(parent),
|
||||
m_name(sub_obj_name),
|
||||
m_type(itVolume),
|
||||
m_volume_type(type),
|
||||
m_is_text_volume(is_text_volume),
|
||||
m_is_svg_volume(is_svg_volume),
|
||||
m_idx(idx),
|
||||
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : ""),
|
||||
m_warning_icon_name(warning_icon_name)
|
||||
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "")
|
||||
{
|
||||
m_bmp = bmp;
|
||||
set_icons();
|
||||
init_container();
|
||||
}
|
||||
|
@ -135,7 +151,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
|
|||
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||
const t_layer_height_range& layer_range,
|
||||
const int idx /*= -1 */,
|
||||
const wxString extruder) :
|
||||
const wxString& extruder) :
|
||||
m_parent(parent),
|
||||
m_type(itLayer),
|
||||
m_idx(idx),
|
||||
|
@ -271,7 +287,7 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps()
|
|||
std::string scaled_bitmap_name = m_name.ToUTF8().data();
|
||||
scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "");
|
||||
|
||||
wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name);
|
||||
wxBitmapBundle *bmp = find_bndl(scaled_bitmap_name);
|
||||
if (bmp == nullptr) {
|
||||
std::vector<wxBitmapBundle*> bmps;
|
||||
for (auto& category : m_opt_categories)
|
||||
|
@ -432,6 +448,8 @@ ObjectDataViewModel::ObjectDataViewModel()
|
|||
m_bitmap_cache = new Slic3r::GUI::BitmapCache;
|
||||
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
|
||||
m_svg_volume_bmps = MenuFactory::get_svg_volume_bitmaps();
|
||||
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
||||
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
|
||||
m_lock_bmp = *get_bmp_bundle(LockIcon);
|
||||
|
@ -461,11 +479,6 @@ void ObjectDataViewModel::Init()
|
|||
AddOutsidePlate();
|
||||
}
|
||||
|
||||
wxBitmapBundle& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name)
|
||||
{
|
||||
return warning_icon_name.empty() ? m_empty_bmp : warning_icon_name == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp;
|
||||
}
|
||||
|
||||
wxDataViewItem ObjectDataViewModel::AddPlate(PartPlate* part_plate, wxString name, bool refresh)
|
||||
{
|
||||
int plate_idx = part_plate ? part_plate->get_index() : -1;
|
||||
|
@ -524,19 +537,23 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode *node)
|
|||
is_volume_node &= (vol_type >= int(ModelVolumeType::MODEL_PART) && vol_type <= int(ModelVolumeType::SUPPORT_ENFORCER));
|
||||
|
||||
if (!node->has_warning_icon() && !node->has_lock()) {
|
||||
node->SetBitmap(is_volume_node ? *m_volume_bmps.at(vol_type) : m_empty_bmp);
|
||||
node->SetBitmap(is_volume_node ? (
|
||||
node->is_text_volume() ? *m_text_volume_bmps.at(vol_type) :
|
||||
node->is_svg_volume() ? *m_svg_volume_bmps.at(vol_type) :
|
||||
*m_volume_bmps.at(vol_type)) : m_empty_bmp);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string scaled_bitmap_name = std::string();
|
||||
if (node->has_warning_icon())
|
||||
scaled_bitmap_name += node->warning_icon_name();
|
||||
if (node->has_lock())
|
||||
if (node->has_lock())
|
||||
scaled_bitmap_name += LockIcon;
|
||||
if (is_volume_node)
|
||||
scaled_bitmap_name += std::to_string(vol_type);
|
||||
scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "-lm");
|
||||
|
||||
wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name);
|
||||
wxBitmapBundle* bmp = find_bndl(scaled_bitmap_name);
|
||||
if (!bmp) {
|
||||
std::vector<wxBitmapBundle*> bmps;
|
||||
if (node->has_warning_icon())
|
||||
|
@ -544,15 +561,19 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode *node)
|
|||
if (node->has_lock())
|
||||
bmps.emplace_back(&m_lock_bmp);
|
||||
if (is_volume_node)
|
||||
bmps.emplace_back(m_volume_bmps[vol_type]);
|
||||
bmps.emplace_back(
|
||||
node->is_text_volume() ? m_text_volume_bmps[vol_type] :
|
||||
node->is_svg_volume() ? m_svg_volume_bmps[vol_type] :
|
||||
m_volume_bmps[vol_type]);
|
||||
bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps);
|
||||
}
|
||||
|
||||
node->SetBitmap(*bmp);
|
||||
}
|
||||
|
||||
void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode *node, bool has_lock)
|
||||
void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock)
|
||||
{
|
||||
node->SetWarningIconName(warning_icon_name);
|
||||
node->SetLock(has_lock);
|
||||
UpdateBitmapForNode(node);
|
||||
}
|
||||
|
@ -578,8 +599,8 @@ wxDataViewItem ObjectDataViewModel::AddObject(ModelObject *model_object, std::st
|
|||
//const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
|
||||
const wxString extruder_str = wxString::Format("%d", extruder);
|
||||
auto obj_node = new ObjectDataViewModelNode(name, extruder_str, plate_idx, model_object);
|
||||
obj_node->SetWarningBitmap(GetWarningBitmap(warning_bitmap), warning_bitmap);
|
||||
UpdateBitmapForNode(obj_node, has_lock);
|
||||
// Add warning icon if detected auto-repaire
|
||||
UpdateBitmapForNode(obj_node, warning_bitmap, has_lock);
|
||||
|
||||
if (plate_node != nullptr) {
|
||||
obj_node->m_parent = plate_node;
|
||||
|
@ -605,6 +626,8 @@ wxDataViewItem ObjectDataViewModel::AddObject(ModelObject *model_object, std::st
|
|||
wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const bool is_text_volume,
|
||||
const bool is_svg_volume,
|
||||
const std::string& warning_icon_name/* = std::string()*/,
|
||||
const int extruder/* = 0*/,
|
||||
const bool create_frst_child/* = true*/)
|
||||
|
@ -620,7 +643,8 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
|||
if (create_frst_child && root->m_volumes_cnt == 0)
|
||||
{
|
||||
const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART;
|
||||
const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, root->m_warning_icon_name), root->m_extruder, 0, root->m_warning_icon_name);
|
||||
const auto node = new ObjectDataViewModelNode(root, root->m_name, type, is_text_volume, is_svg_volume, root->m_extruder, 0);
|
||||
UpdateBitmapForNode(node, root->m_warning_icon_name, root->has_lock());
|
||||
|
||||
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
||||
// notify control
|
||||
|
@ -643,14 +667,16 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
|||
extruder_str = wxString::Format("%d", extruder);
|
||||
}
|
||||
|
||||
const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, warning_icon_name),
|
||||
extruder_str, root->m_volumes_cnt, warning_icon_name);
|
||||
const auto node = new ObjectDataViewModelNode(root, name, volume_type, is_text_volume, is_svg_volume, extruder_str, root->m_volumes_cnt);
|
||||
UpdateBitmapForNode(node, warning_icon_name, root->has_lock() && volume_type < ModelVolumeType::PARAMETER_MODIFIER);
|
||||
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
||||
|
||||
// if part with errors is added, but object wasn't marked, then mark it
|
||||
if (!warning_icon_name.empty() && warning_icon_name != root->m_warning_icon_name &&
|
||||
(root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) )
|
||||
root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
(root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) ) {
|
||||
root->SetWarningIconName(warning_icon_name);
|
||||
UpdateBitmapForNode(root);
|
||||
}
|
||||
|
||||
// notify control
|
||||
const wxDataViewItem child((void*)node);
|
||||
|
@ -2297,6 +2323,8 @@ void ObjectDataViewModel::SetSinkState(const bool painted, wxDataViewItem obj_it
|
|||
void ObjectDataViewModel::UpdateBitmaps()
|
||||
{
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
|
||||
m_svg_volume_bmps = MenuFactory::get_svg_volume_bitmaps();
|
||||
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
||||
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
|
||||
m_lock_bmp = *get_bmp_bundle(LockIcon);
|
||||
|
@ -2318,10 +2346,8 @@ void ObjectDataViewModel::UpdateBitmaps()
|
|||
switch (node->m_type)
|
||||
{
|
||||
case itObject:
|
||||
if (node->m_bmp.IsOk()) node->m_bmp = GetWarningBitmap(node->m_warning_icon_name);
|
||||
break;
|
||||
case itVolume:
|
||||
node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name);
|
||||
UpdateBitmapForNode(node);
|
||||
break;
|
||||
case itLayerRoot:
|
||||
node->m_bmp = *get_bmp_bundle(LayerRootIcon);
|
||||
|
@ -2339,27 +2365,6 @@ void ObjectDataViewModel::UpdateBitmaps()
|
|||
}
|
||||
}
|
||||
|
||||
wxBitmapBundle ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/)
|
||||
{
|
||||
if (warning_icon_name.empty())
|
||||
return *m_volume_bmps[static_cast<int>(vol_type)];
|
||||
|
||||
std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast<int>(vol_type));
|
||||
scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm");
|
||||
|
||||
wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name);
|
||||
if (bmp == nullptr) {
|
||||
std::vector<wxBitmapBundle*> bmps;
|
||||
|
||||
bmps.emplace_back(&GetWarningBitmap(warning_icon_name));
|
||||
bmps.emplace_back(m_volume_bmps[static_cast<int>(vol_type)]);
|
||||
|
||||
bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps);
|
||||
}
|
||||
|
||||
return *bmp;
|
||||
}
|
||||
|
||||
void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name)
|
||||
{
|
||||
if (!item.IsOk())
|
||||
|
@ -2367,13 +2372,14 @@ void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::
|
|||
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
|
||||
|
||||
if (node->GetType() & itObject) {
|
||||
node->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
UpdateBitmapForNode(node, warning_icon_name, node->has_lock());
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->GetType() & itVolume) {
|
||||
node->SetWarningBitmap(GetVolumeIcon(node->GetVolumeType(), warning_icon_name), warning_icon_name);
|
||||
node->GetParent()->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
UpdateBitmapForNode(node, warning_icon_name, node->has_lock());
|
||||
if (ObjectDataViewModelNode* parent = node->GetParent())
|
||||
UpdateBitmapForNode(parent, warning_icon_name, parent->has_lock());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2388,12 +2394,9 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo
|
|||
if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject)))
|
||||
return;
|
||||
|
||||
if (node->GetType() & itVolume) {
|
||||
node->SetWarningBitmap(*m_volume_bmps[static_cast<int>(node->volume_type())], "");
|
||||
return;
|
||||
}
|
||||
node->SetWarningIconName(std::string());
|
||||
UpdateBitmapForNode(node);
|
||||
|
||||
node->SetWarningBitmap(wxNullBitmap, "");
|
||||
if (unmark_object)
|
||||
{
|
||||
wxDataViewItemArray children;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_ObjectDataViewModel_hpp_
|
||||
#define slic3r_GUI_ObjectDataViewModel_hpp_
|
||||
|
||||
|
@ -97,6 +101,8 @@ class ObjectDataViewModelNode
|
|||
|
||||
std::string m_action_icon_name = "";
|
||||
ModelVolumeType m_volume_type = ModelVolumeType(-1);
|
||||
bool m_is_text_volume{ false };
|
||||
bool m_is_svg_volume{false};
|
||||
InfoItemType m_info_item_type {InfoItemType::Undef};
|
||||
bool m_action_enable = false; // can undo all settings
|
||||
// BBS
|
||||
|
@ -127,15 +133,15 @@ public:
|
|||
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||
const wxString& sub_obj_name,
|
||||
Slic3r::ModelVolumeType type,
|
||||
const wxBitmapBundle& bmp,
|
||||
const bool is_text_volume,
|
||||
const bool is_svg_volume,
|
||||
const wxString& extruder,
|
||||
const int idx = -1,
|
||||
const std::string& warning_icon_name = std::string());
|
||||
const int idx = -1 );
|
||||
|
||||
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||
const t_layer_height_range& layer_range,
|
||||
const int idx = -1,
|
||||
const wxString extruder = "" );
|
||||
const wxString& extruder = wxEmptyString );
|
||||
|
||||
ObjectDataViewModelNode(PartPlate* part_plate, wxString name);
|
||||
|
||||
|
@ -225,9 +231,9 @@ public:
|
|||
void SetVolumeType(ModelVolumeType type) { m_volume_type = type; }
|
||||
void SetBitmap(const wxBitmapBundle &icon) { m_bmp = icon; }
|
||||
void SetExtruder(const wxString &extruder) { m_extruder = extruder; }
|
||||
void SetWarningBitmap(const wxBitmapBundle& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; }
|
||||
void SetLock(bool has_lock) { m_has_lock = has_lock; }
|
||||
const wxBitmapBundle& GetBitmap() const { return m_bmp; }
|
||||
void SetWarningIconName(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; }
|
||||
void SetLock(bool has_lock) { m_has_lock = has_lock; }
|
||||
const wxBitmapBundle& GetBitmap() const { return m_bmp; }
|
||||
const wxString& GetName() const { return m_name; }
|
||||
ItemType GetType() const { return m_type; }
|
||||
InfoItemType GetInfoItemType() const { return m_info_item_type; }
|
||||
|
@ -293,6 +299,8 @@ public:
|
|||
void update_settings_digest_bitmaps();
|
||||
bool update_settings_digest(const std::vector<std::string>& categories);
|
||||
int volume_type() const { return int(m_volume_type); }
|
||||
bool is_text_volume() const { return m_is_text_volume; }
|
||||
bool is_svg_volume() const { return m_is_svg_volume; }
|
||||
void sys_color_changed();
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -320,6 +328,8 @@ class ObjectDataViewModel :public wxDataViewModel
|
|||
std::vector<ObjectDataViewModelNode*> m_plates;
|
||||
std::vector<ObjectDataViewModelNode*> m_objects;
|
||||
std::vector<wxBitmapBundle*> m_volume_bmps;
|
||||
std::vector<wxBitmapBundle*> m_text_volume_bmps;
|
||||
std::vector<wxBitmapBundle*> m_svg_volume_bmps;
|
||||
std::map<InfoItemType, wxBitmapBundle*> m_info_bmps;
|
||||
wxBitmapBundle m_empty_bmp;
|
||||
wxBitmapBundle m_warning_bmp;
|
||||
|
@ -343,6 +353,8 @@ public:
|
|||
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const bool is_text_volume,
|
||||
const bool is_svg_volume,
|
||||
const std::string& warning_icon_name = std::string(),
|
||||
const int extruder = 0,
|
||||
const bool create_frst_child = true);
|
||||
|
@ -469,8 +481,6 @@ public:
|
|||
// Rescale bitmaps for existing Items
|
||||
void UpdateBitmaps();
|
||||
|
||||
wxBitmapBundle GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
|
||||
const std::string& warning_icon_name = std::string());
|
||||
void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
||||
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
|
||||
void UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
||||
|
@ -500,12 +510,11 @@ private:
|
|||
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
|
||||
void AddAllChildren(const wxDataViewItem& parent);
|
||||
|
||||
wxBitmapBundle& GetWarningBitmap(const std::string& warning_icon_name);
|
||||
void ReparentObject(ObjectDataViewModelNode* plate, ObjectDataViewModelNode* object);
|
||||
wxDataViewItem AddOutsidePlate(bool refresh = true);
|
||||
|
||||
void UpdateBitmapForNode(ObjectDataViewModelNode *node);
|
||||
void UpdateBitmapForNode(ObjectDataViewModelNode *node, bool has_lock);
|
||||
void UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -108,8 +108,11 @@
|
|||
#include "Jobs/FillBedJob.hpp"
|
||||
#include "Jobs/RotoptimizeJob.hpp"
|
||||
#include "Jobs/SLAImportJob.hpp"
|
||||
#include "Jobs/SLAImportDialog.hpp"
|
||||
#include "Jobs/PrintJob.hpp"
|
||||
#include "Jobs/NotificationProgressIndicator.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "BackgroundSlicingProcess.hpp"
|
||||
#include "SelectMachine.hpp"
|
||||
#include "SendToPrinter.hpp"
|
||||
|
@ -128,6 +131,7 @@
|
|||
#include "MsgDialog.hpp"
|
||||
#include "ProjectDirtyStateManager.hpp"
|
||||
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
|
||||
#include "Gizmos/GLGizmoSVG.hpp" // Drop SVG file
|
||||
#include "Gizmos/GizmoObjectManipulation.hpp"
|
||||
|
||||
// BBS
|
||||
|
@ -1897,39 +1901,38 @@ void Sidebar::can_search()
|
|||
class PlaterDropTarget : public wxFileDropTarget
|
||||
{
|
||||
public:
|
||||
PlaterDropTarget(Plater* plater) : m_plater(plater) { this->SetDefaultAction(wxDragCopy); }
|
||||
PlaterDropTarget(MainFrame& mainframe, Plater& plater) : m_mainframe(mainframe), m_plater(plater) {
|
||||
this->SetDefaultAction(wxDragCopy);
|
||||
}
|
||||
|
||||
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames);
|
||||
|
||||
void handleOnIdle(wxIdleEvent & event);
|
||||
|
||||
private:
|
||||
Plater* m_plater;
|
||||
wxArrayString m_filenames;
|
||||
MainFrame& m_mainframe;
|
||||
Plater& m_plater;
|
||||
};
|
||||
|
||||
bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
|
||||
namespace {
|
||||
bool emboss_svg(Plater& plater, const wxString &svg_file, const Vec2d& mouse_drop_position)
|
||||
{
|
||||
#ifdef WIN32
|
||||
// hides the system icon
|
||||
this->MSWUpdateDragImageOnLeave();
|
||||
#endif // WIN32
|
||||
std::string svg_file_str = into_u8(svg_file);
|
||||
GLCanvas3D* canvas = plater.canvas3D();
|
||||
if (canvas == nullptr)
|
||||
return false;
|
||||
auto base_svg = canvas->get_gizmos_manager().get_gizmo(GLGizmosManager::Svg);
|
||||
if (base_svg == nullptr)
|
||||
return false;
|
||||
GLGizmoSVG* svg = dynamic_cast<GLGizmoSVG *>(base_svg);
|
||||
if (svg == nullptr)
|
||||
return false;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": drag %1% files into app")%filenames.size();
|
||||
m_filenames = filenames;
|
||||
wxGetApp().Bind(wxEVT_IDLE, &PlaterDropTarget::handleOnIdle, this);
|
||||
return true;
|
||||
// Refresh hover state to find surface point under mouse
|
||||
wxMouseEvent evt(wxEVT_MOTION);
|
||||
evt.SetPosition(wxPoint(mouse_drop_position.x(), mouse_drop_position.y()));
|
||||
canvas->on_mouse(evt); // call render where is call GLCanvas3D::_picking_pass()
|
||||
|
||||
return svg->create_volume(svg_file_str, mouse_drop_position, ModelVolumeType::MODEL_PART);
|
||||
}
|
||||
|
||||
void PlaterDropTarget::handleOnIdle(wxIdleEvent &event)
|
||||
{
|
||||
wxGetApp().mainframe->Raise();
|
||||
wxGetApp().Unbind(wxEVT_IDLE, &PlaterDropTarget::handleOnIdle, this);
|
||||
if (m_plater != nullptr) {
|
||||
m_plater->load_files(m_filenames);
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
//m_filenames.clear();
|
||||
}
|
||||
|
||||
// State to manage showing after export notifications and device ejecting
|
||||
|
@ -2030,70 +2033,15 @@ struct Plater::priv
|
|||
BackgroundSlicingProcess background_process;
|
||||
bool suppressed_backround_processing_update { false };
|
||||
|
||||
// Jobs defined inside the group class will be managed so that only one can
|
||||
// run at a time. Also, the background process will be stopped if a job is
|
||||
// started. It is up the the plater to ensure that the background slicing
|
||||
// can't be restarted while a ui job is still running.
|
||||
class Jobs: public ExclusiveJobGroup
|
||||
{
|
||||
priv *m;
|
||||
size_t m_arrange_id, m_fill_bed_id, m_rotoptimize_id, m_sla_import_id, m_orient_id;
|
||||
std::shared_ptr<NotificationProgressIndicator> m_pri;
|
||||
//BBS
|
||||
size_t m_print_id;
|
||||
|
||||
void before_start() override { m->background_process.stop(); }
|
||||
|
||||
public:
|
||||
Jobs(priv *_m) :
|
||||
m(_m),
|
||||
m_pri{std::make_shared<NotificationProgressIndicator>(m->notification_manager.get())}
|
||||
{
|
||||
m_arrange_id = add_job(std::make_unique<ArrangeJob>(m_pri, m->q));
|
||||
m_orient_id = add_job(std::make_unique<OrientJob>(m_pri, m->q));
|
||||
m_fill_bed_id = add_job(std::make_unique<FillBedJob>(m_pri, m->q));
|
||||
m_rotoptimize_id = add_job(std::make_unique<RotoptimizeJob>(m_pri, m->q));
|
||||
m_sla_import_id = add_job(std::make_unique<SLAImportJob>(m_pri, m->q));
|
||||
//BBS add print id
|
||||
m_print_id = add_job(std::make_unique<PrintJob>(m_pri, m->q));
|
||||
}
|
||||
|
||||
void arrange()
|
||||
{
|
||||
m->take_snapshot("Arrange");
|
||||
start(m_arrange_id);
|
||||
}
|
||||
|
||||
void orient()
|
||||
{
|
||||
m->take_snapshot("Orient");
|
||||
start(m_orient_id);
|
||||
}
|
||||
|
||||
void fill_bed()
|
||||
{
|
||||
m->take_snapshot("Fill bed");
|
||||
start(m_fill_bed_id);
|
||||
}
|
||||
|
||||
void optimize_rotation()
|
||||
{
|
||||
m->take_snapshot("Optimize Rotation");
|
||||
start(m_rotoptimize_id);
|
||||
}
|
||||
|
||||
void import_sla_arch()
|
||||
{
|
||||
m->take_snapshot("Import SLA archive");
|
||||
start(m_sla_import_id);
|
||||
}
|
||||
|
||||
//BBS bbl printing job
|
||||
void print()
|
||||
{
|
||||
start(m_print_id);
|
||||
}
|
||||
} m_ui_jobs;
|
||||
// TODO: A mechanism would be useful for blocking the plater interactions:
|
||||
// objects would be frozen for the user. In case of arrange, an animation
|
||||
// could be shown, or with the optimize orientations, partial results
|
||||
// could be displayed.
|
||||
//
|
||||
// UIThreadWorker can be used as a replacement for BoostThreadWorker if
|
||||
// no additional worker threads are desired (useful for debugging or profiling)
|
||||
PlaterWorker<BoostThreadWorker> m_worker;
|
||||
SLAImportDialog * m_sla_import_dlg;
|
||||
|
||||
int m_job_prepare_state;
|
||||
|
||||
|
@ -2375,6 +2323,7 @@ struct Plater::priv
|
|||
void on_modify_filament(SimpleEvent &);
|
||||
|
||||
void on_object_select(SimpleEvent&);
|
||||
void show_right_click_menu(Vec2d mouse_position, wxMenu *menu);
|
||||
void on_right_click(RBtnEvent&);
|
||||
//BBS: add model repair
|
||||
void on_repair_model(wxCommandEvent &event);
|
||||
|
@ -2424,7 +2373,6 @@ struct Plater::priv
|
|||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_edit_text() const;
|
||||
bool can_add_plate() const;
|
||||
bool can_delete_plate() const;
|
||||
bool can_increase_instances() const;
|
||||
|
@ -2538,6 +2486,37 @@ const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::ica
|
|||
const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_prusa(".*bbl", std::regex::icase);
|
||||
|
||||
bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
|
||||
{
|
||||
#ifdef WIN32
|
||||
// hides the system icon
|
||||
this->MSWUpdateDragImageOnLeave();
|
||||
#endif // WIN32
|
||||
|
||||
m_mainframe.Raise();
|
||||
m_mainframe.select_tab(size_t(MainFrame::tp3DEditor));
|
||||
if (wxGetApp().is_editor())
|
||||
m_plater.select_view_3D("3D");
|
||||
|
||||
// When only one .svg file is dropped on scene
|
||||
if (filenames.size() == 1) {
|
||||
const wxString &filename = filenames.Last();
|
||||
const wxString file_extension = filename.substr(filename.length() - 4);
|
||||
if (file_extension.CmpNoCase(".svg") == 0) {
|
||||
// BBS: GUI refactor: move sidebar to the left
|
||||
const wxPoint offset = m_plater.GetPosition() + m_plater.p->current_panel->GetPosition();
|
||||
Vec2d mouse_position(x - offset.x, y - offset.y);
|
||||
// Scale for retina displays
|
||||
const GLCanvas3D *canvas = m_plater.canvas3D();
|
||||
canvas->apply_retina_scale(mouse_position);
|
||||
return emboss_svg(m_plater, filename, mouse_position);
|
||||
}
|
||||
}
|
||||
bool res = m_plater.load_files(filenames);
|
||||
m_mainframe.update_title();
|
||||
return res;
|
||||
}
|
||||
|
||||
Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
: q(q)
|
||||
, main_frame(main_frame)
|
||||
|
@ -2558,7 +2537,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
}))
|
||||
, sidebar(new Sidebar(q))
|
||||
, notification_manager(std::make_unique<NotificationManager>(q))
|
||||
, m_ui_jobs(this)
|
||||
, m_worker{q, std::make_unique<NotificationProgressIndicator>(notification_manager.get()), "ui_worker"}
|
||||
, m_sla_import_dlg{new SLAImportDialog{q}}
|
||||
, m_job_prepare_state(Job::JobPrepareState::PREPARE_STATE_DEFAULT)
|
||||
, delayed_scene_refresh(false)
|
||||
, collapse_toolbar(GLToolbar::Normal, "Collapse")
|
||||
|
@ -2873,7 +2853,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
}
|
||||
|
||||
// Drop target:
|
||||
q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership
|
||||
q->SetDropTarget(new PlaterDropTarget(*main_frame, *q)); // if my understanding is right, wxWindow takes the owenership
|
||||
q->Layout();
|
||||
|
||||
apply_color_mode();
|
||||
|
@ -4515,7 +4495,7 @@ void Plater::priv::remove(size_t obj_idx)
|
|||
if (view3D->is_layers_editing_enabled())
|
||||
view3D->enable_layers_editing(false);
|
||||
|
||||
m_ui_jobs.cancel_all();
|
||||
m_worker.cancel_all();
|
||||
model.delete_object(obj_idx);
|
||||
//BBS: notify partplate the instance removed
|
||||
partplate_list.notify_instance_removed(obj_idx, -1);
|
||||
|
@ -4546,7 +4526,7 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx, bool refresh_immedia
|
|||
if (!obj->name.empty())
|
||||
snapshot_label += ": " + obj->name;
|
||||
Plater::TakeSnapshot snapshot(q, snapshot_label);
|
||||
m_ui_jobs.cancel_all();
|
||||
m_worker.cancel_all();
|
||||
|
||||
if (obj->is_cut())
|
||||
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
||||
|
@ -4576,7 +4556,7 @@ void Plater::priv::delete_all_objects_from_model()
|
|||
|
||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||
|
||||
m_ui_jobs.cancel_all();
|
||||
m_worker.cancel_all();
|
||||
|
||||
// Stop and reset the Print content.
|
||||
background_process.reset();
|
||||
|
@ -4616,7 +4596,7 @@ void Plater::priv::reset(bool apply_presets_change)
|
|||
|
||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||
|
||||
m_ui_jobs.cancel_all();
|
||||
m_worker.cancel_all();
|
||||
|
||||
//BBS: clear the partplate list's object before object cleared
|
||||
partplate_list.reinit();
|
||||
|
@ -5037,7 +5017,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||
// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState.
|
||||
bool Plater::priv::restart_background_process(unsigned int state)
|
||||
{
|
||||
if (m_ui_jobs.is_any_running()) {
|
||||
if (!m_worker.is_idle()) {
|
||||
// Avoid a race condition
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: ui jobs running, return false")%__LINE__;
|
||||
return false;
|
||||
|
@ -5237,7 +5217,7 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
|
|||
new_volume->set_type(old_volume->type());
|
||||
new_volume->set_material_id(old_volume->material_id());
|
||||
new_volume->set_transformation(old_volume->get_transformation());
|
||||
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
|
||||
new_volume->translate(new_volume->get_transformation().get_matrix_no_offset() * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
|
||||
assert(!old_volume->source.is_converted_from_inches || !old_volume->source.is_converted_from_meters);
|
||||
if (old_volume->source.is_converted_from_inches)
|
||||
new_volume->convert_from_imperial_units();
|
||||
|
@ -5605,8 +5585,8 @@ void Plater::priv::reload_from_disk()
|
|||
|
||||
Transform3d transform = Transform3d::Identity();
|
||||
transform.translate(new_volume->source.mesh_offset - old_volume->source.mesh_offset);
|
||||
new_volume->set_transformation(old_volume->get_transformation().get_matrix() * old_volume->source.transform.get_matrix(true) *
|
||||
transform * new_volume->source.transform.get_matrix(true).inverse());
|
||||
new_volume->set_transformation(old_volume->get_transformation().get_matrix() * old_volume->source.transform.get_matrix_no_offset() *
|
||||
transform * new_volume->source.transform.get_matrix_no_offset().inverse());
|
||||
|
||||
new_volume->source.object_idx = old_volume->source.object_idx;
|
||||
new_volume->source.volume_idx = old_volume->source.volume_idx;
|
||||
|
@ -5679,7 +5659,7 @@ void Plater::priv::reload_from_disk()
|
|||
new_volume->set_type(old_volume->type());
|
||||
new_volume->set_material_id(old_volume->material_id());
|
||||
new_volume->set_transformation(old_volume->get_transformation());
|
||||
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
|
||||
new_volume->translate(new_volume->get_transformation().get_matrix_no_offset() * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
|
||||
new_volume->source.object_idx = old_volume->source.object_idx;
|
||||
new_volume->source.volume_idx = old_volume->source.volume_idx;
|
||||
assert(! old_volume->source.is_converted_from_inches || ! old_volume->source.is_converted_from_meters);
|
||||
|
@ -6208,7 +6188,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
std::string title_text = _u8L("Slicing");
|
||||
evt.status.text = title_text + evt.status.text;
|
||||
if (evt.status.percent >= 0) {
|
||||
if (m_ui_jobs.is_any_running()) {
|
||||
if (!m_worker.is_idle()) {
|
||||
// Avoid a race condition
|
||||
return;
|
||||
}
|
||||
|
@ -6584,7 +6564,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||
}
|
||||
}
|
||||
}
|
||||
q->SetDropTarget(new PlaterDropTarget(q));
|
||||
q->SetDropTarget(new PlaterDropTarget(*main_frame, *q));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -7003,6 +6983,24 @@ static void get_position(wxWindowBase* child, wxWindowBase* until_parent, int& x
|
|||
y = res_y;
|
||||
}
|
||||
|
||||
void Plater::priv::show_right_click_menu(Vec2d mouse_position, wxMenu *menu)
|
||||
{
|
||||
// BBS: GUI refactor: move sidebar to the left
|
||||
int x, y;
|
||||
get_position(current_panel, wxGetApp().mainframe, x, y);
|
||||
wxPoint position(static_cast<int>(mouse_position.x() + x), static_cast<int>(mouse_position.y() + y));
|
||||
#ifdef __linux__
|
||||
// For some reason on Linux the menu isn't displayed if position is
|
||||
// specified (even though the position is sane).
|
||||
position = wxDefaultPosition;
|
||||
#endif
|
||||
GLCanvas3D &canvas = *q->canvas3D();
|
||||
canvas.apply_retina_scale(mouse_position);
|
||||
canvas.set_popup_menu_position(mouse_position);
|
||||
q->PopupMenu(menu, position);
|
||||
canvas.clear_popup_menu_position();
|
||||
}
|
||||
|
||||
void Plater::priv::on_right_click(RBtnEvent& evt)
|
||||
{
|
||||
int obj_idx = get_selected_object_idx();
|
||||
|
@ -7048,41 +7046,30 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
|
|||
menu = is_some_full_instances ? menus.assemble_object_menu() :
|
||||
is_part ? menus.assemble_part_menu() : menus.assemble_multi_selection_menu();
|
||||
} else {
|
||||
menu = is_some_full_instances ? menus.object_menu() :
|
||||
is_part ? menus.part_menu() : menus.multi_selection_menu();
|
||||
if (is_some_full_instances)
|
||||
menu = printer_technology == ptSLA ? menus.sla_object_menu() : menus.object_menu();
|
||||
else if (is_part) {
|
||||
const GLVolume* gl_volume = selection.get_first_volume();
|
||||
const ModelVolume *model_volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
menu = (model_volume != nullptr && model_volume->is_text()) ? menus.text_part_menu() :
|
||||
(model_volume != nullptr && model_volume->is_svg()) ? menus.svg_part_menu() :
|
||||
menus.part_menu();
|
||||
} else
|
||||
menu = menus.multi_selection_menu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (q != nullptr && menu) {
|
||||
#ifdef __linux__
|
||||
// For some reason on Linux the menu isn't displayed if position is specified
|
||||
// (even though the position is sane).
|
||||
q->PopupMenu(menu);
|
||||
#else
|
||||
//BBS: GUI refactor: move sidebar to the left
|
||||
int x, y;
|
||||
get_position(current_panel, wxGetApp().mainframe, x, y);
|
||||
q->PopupMenu(menu, (int) evt.data.first.x() + x, (int) evt.data.first.y() + y);
|
||||
//q->PopupMenu(menu);
|
||||
#endif
|
||||
show_right_click_menu(evt.data.first, menu);
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: add part plate related logic
|
||||
void Plater::priv::on_plate_right_click(RBtnPlateEvent& evt)
|
||||
{
|
||||
wxMenu* menu = menus.plate_menu();
|
||||
|
||||
#ifdef __linux__
|
||||
q->PopupMenu(menu);
|
||||
#else
|
||||
//BBS: GUI refactor: move sidebar to the left
|
||||
int x, y;
|
||||
get_position(current_panel, wxGetApp().mainframe, x, y);
|
||||
q->PopupMenu(menu, (int) evt.data.first.x() + x, (int) evt.data.first.y() + y);
|
||||
//q->PopupMenu(menu);
|
||||
#endif
|
||||
wxMenu *menu = menus.plate_menu();
|
||||
show_right_click_menu(evt.data.first, menu);
|
||||
}
|
||||
|
||||
void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)
|
||||
|
@ -7317,7 +7304,11 @@ void Plater::priv::init_notification_manager()
|
|||
|
||||
void Plater::orient()
|
||||
{
|
||||
p->m_ui_jobs.orient();
|
||||
auto &w = get_ui_job_worker();
|
||||
if (w.is_idle()) {
|
||||
p->take_snapshot(_u8L("Orient"));
|
||||
replace_job(w, std::make_unique<OrientJob>());
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: add job state related functions
|
||||
|
@ -7672,24 +7663,6 @@ bool Plater::priv::can_delete_all() const
|
|||
return !model.objects.empty();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_edit_text() const
|
||||
{
|
||||
const Selection &selection = view3D->get_canvas3d()->get_selection();
|
||||
if (selection.is_single_full_instance())
|
||||
return true;
|
||||
|
||||
if (selection.is_single_volume()) {
|
||||
const GLVolume *gl_volume = selection.get_first_volume();
|
||||
int out_object_idx = gl_volume->object_idx();
|
||||
ModelObject * model_object = selection.get_model()->objects[out_object_idx];
|
||||
int out_volume_idx = gl_volume->volume_idx();
|
||||
ModelVolume * model_volume = model_object->volumes[out_volume_idx];
|
||||
if (model_volume)
|
||||
return !model_volume->get_text_info().m_text.empty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Plater::priv::can_add_plate() const
|
||||
{
|
||||
return q->get_partplate_list().get_plate_count() < PartPlateList::MAX_PLATES_COUNT;
|
||||
|
@ -7738,7 +7711,7 @@ bool Plater::priv::can_simplify() const
|
|||
|
||||
bool Plater::priv::can_increase_instances() const
|
||||
{
|
||||
if (m_ui_jobs.is_any_running()
|
||||
if (!m_worker.is_idle()
|
||||
|| q->get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode())
|
||||
return false;
|
||||
|
||||
|
@ -7750,7 +7723,7 @@ bool Plater::priv::can_increase_instances() const
|
|||
|
||||
bool Plater::priv::can_decrease_instances() const
|
||||
{
|
||||
if (m_ui_jobs.is_any_running()
|
||||
if (!m_worker.is_idle()
|
||||
|| q->get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode())
|
||||
return false;
|
||||
|
||||
|
@ -7771,7 +7744,7 @@ bool Plater::priv::can_split_to_volumes() const
|
|||
|
||||
bool Plater::priv::can_arrange() const
|
||||
{
|
||||
return !model.objects.empty() && !m_ui_jobs.is_any_running();
|
||||
return !model.objects.empty() && m_worker.is_idle();
|
||||
}
|
||||
|
||||
bool Plater::priv::layers_height_allowed() const
|
||||
|
@ -9324,8 +9297,11 @@ void Plater::calib_VFA(const Calib_Params& params)
|
|||
BuildVolume_Type Plater::get_build_volume_type() const { return p->bed.get_build_volume_type(); }
|
||||
void Plater::import_sl1_archive()
|
||||
{
|
||||
if (!p->m_ui_jobs.is_any_running())
|
||||
p->m_ui_jobs.import_sla_arch();
|
||||
auto &w = get_ui_job_worker();
|
||||
if (w.is_idle() && p->m_sla_import_dlg->ShowModal() == wxID_OK) {
|
||||
p->take_snapshot(_u8L("Import SLA archive"));
|
||||
replace_job(w, std::make_unique<SLAImportJob>(p->m_sla_import_dlg));
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::extract_config_from_project()
|
||||
|
@ -10150,12 +10126,9 @@ void Plater::update(bool conside_update_flag, bool force_background_processing_u
|
|||
|
||||
void Plater::object_list_changed() { p->object_list_changed(); }
|
||||
|
||||
void Plater::stop_jobs() { p->m_ui_jobs.stop_all(); }
|
||||
Worker &Plater::get_ui_job_worker() { return p->m_worker; }
|
||||
|
||||
bool Plater::is_any_job_running() const
|
||||
{
|
||||
return p->m_ui_jobs.is_any_running();
|
||||
}
|
||||
const Worker &Plater::get_ui_job_worker() const { return p->m_worker; }
|
||||
|
||||
void Plater::update_ui_from_settings() { p->update_ui_from_settings(); }
|
||||
|
||||
|
@ -10265,7 +10238,7 @@ void Plater::set_selected_visible(bool visible)
|
|||
return;
|
||||
|
||||
Plater::TakeSnapshot snapshot(this, "Set Selected Objects Visible in AssembleView");
|
||||
p->m_ui_jobs.cancel_all();
|
||||
get_ui_job_worker().cancel_all();
|
||||
|
||||
p->get_current_canvas3D()->set_selected_visible(visible);
|
||||
}
|
||||
|
@ -10283,7 +10256,7 @@ void Plater::remove_selected()
|
|||
return;
|
||||
|
||||
Plater::TakeSnapshot snapshot(this, "Delete Selected Objects");
|
||||
p->m_ui_jobs.cancel_all();
|
||||
get_ui_job_worker().cancel_all();
|
||||
|
||||
//BBS delete current selected
|
||||
// p->view3D->delete_selected();
|
||||
|
@ -10403,8 +10376,11 @@ void Plater::set_number_of_copies(/*size_t num*/)
|
|||
|
||||
void Plater::fill_bed_with_instances()
|
||||
{
|
||||
if (!p->m_ui_jobs.is_any_running())
|
||||
p->m_ui_jobs.fill_bed();
|
||||
auto &w = get_ui_job_worker();
|
||||
if (w.is_idle()) {
|
||||
p->take_snapshot(_u8L("Arrange"));
|
||||
replace_job(w, std::make_unique<FillBedJob>());
|
||||
}
|
||||
}
|
||||
|
||||
bool Plater::is_selection_empty() const
|
||||
|
@ -10944,6 +10920,110 @@ void Plater::export_stl(bool extended, bool selection_only, bool multi_stls)
|
|||
}
|
||||
}*/
|
||||
|
||||
namespace {
|
||||
std::string get_file_name(const std::string &file_path)
|
||||
{
|
||||
size_t pos_last_delimiter = file_path.find_last_of("/\\");
|
||||
size_t pos_point = file_path.find_last_of('.');
|
||||
size_t offset = pos_last_delimiter + 1;
|
||||
size_t count = pos_point - pos_last_delimiter - 1;
|
||||
return file_path.substr(offset, count);
|
||||
}
|
||||
using SvgFile = EmbossShape::SvgFile;
|
||||
using SvgFiles = std::vector<SvgFile*>;
|
||||
std::string create_unique_3mf_filepath(const std::string &file, const SvgFiles svgs)
|
||||
{
|
||||
// const std::string MODEL_FOLDER = "3D/"; // copy from file 3mf.cpp
|
||||
std::string path_in_3mf = "3D/" + file + ".svg";
|
||||
size_t suffix_number = 0;
|
||||
bool is_unique = false;
|
||||
do{
|
||||
is_unique = true;
|
||||
path_in_3mf = "3D/" + file + ((suffix_number++)? ("_" + std::to_string(suffix_number)) : "") + ".svg";
|
||||
for (SvgFile *svgfile : svgs) {
|
||||
if (svgfile->path_in_3mf.empty())
|
||||
continue;
|
||||
if (svgfile->path_in_3mf.compare(path_in_3mf) == 0) {
|
||||
is_unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!is_unique);
|
||||
return path_in_3mf;
|
||||
}
|
||||
|
||||
bool set_by_local_path(SvgFile &svg, const SvgFiles& svgs)
|
||||
{
|
||||
// Try to find already used svg file
|
||||
for (SvgFile *svg_ : svgs) {
|
||||
if (svg_->path_in_3mf.empty())
|
||||
continue;
|
||||
if (svg.path.compare(svg_->path) == 0) {
|
||||
svg.path_in_3mf = svg_->path_in_3mf;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function to secure private data before store to 3mf
|
||||
/// </summary>
|
||||
/// <param name="model">Data(also private) to clean before publishing</param>
|
||||
void publish(Model &model, SaveStrategy strategy) {
|
||||
|
||||
// SVG file publishing
|
||||
bool exist_new = false;
|
||||
SvgFiles svgfiles;
|
||||
for (ModelObject *object: model.objects){
|
||||
for (ModelVolume *volume : object->volumes) {
|
||||
if (!volume->emboss_shape.has_value())
|
||||
continue;
|
||||
if (volume->text_configuration.has_value())
|
||||
continue; // text dosen't have svg path
|
||||
|
||||
assert(volume->emboss_shape->svg_file.has_value());
|
||||
if (!volume->emboss_shape->svg_file.has_value())
|
||||
continue;
|
||||
|
||||
SvgFile* svg = &(*volume->emboss_shape->svg_file);
|
||||
if (svg->path_in_3mf.empty())
|
||||
exist_new = true;
|
||||
svgfiles.push_back(svg);
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: don't show this in silence mode
|
||||
if (exist_new && !(strategy & SaveStrategy::Silence)) {
|
||||
MessageDialog dialog(nullptr,
|
||||
_L("Are you sure you want to store original SVGs with their local paths into the 3MF file?\n"
|
||||
"If you hit 'NO', all SVGs in the project will not be editable any more."),
|
||||
_L("Private protection"), wxYES_NO | wxICON_QUESTION);
|
||||
if (dialog.ShowModal() == wxID_NO){
|
||||
for (ModelObject *object : model.objects)
|
||||
for (ModelVolume *volume : object->volumes)
|
||||
if (volume->emboss_shape.has_value())
|
||||
volume->emboss_shape.reset();
|
||||
}
|
||||
}
|
||||
|
||||
for (SvgFile* svgfile : svgfiles){
|
||||
if (!svgfile->path_in_3mf.empty())
|
||||
continue; // already suggested path (previous save)
|
||||
|
||||
// create unique name for svgs, when local path differ
|
||||
std::string filename = "unknown";
|
||||
if (!svgfile->path.empty()) {
|
||||
if (set_by_local_path(*svgfile, svgfiles))
|
||||
continue;
|
||||
// check whether original filename is already in:
|
||||
filename = get_file_name(svgfile->path);
|
||||
}
|
||||
svgfile->path_in_3mf = create_unique_3mf_filepath(filename, svgfiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BBS: backup
|
||||
int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy strategy, int export_plate_idx, Export3mfProgressFn proFn)
|
||||
{
|
||||
|
@ -10963,6 +11043,10 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy
|
|||
if (!path.Lower().EndsWith(".3mf"))
|
||||
return -1;
|
||||
|
||||
// take care about private data stored into .3mf
|
||||
// modify model
|
||||
publish(p->model, strategy);
|
||||
|
||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||
const std::string path_u8 = into_u8(path);
|
||||
wxBusyCursor wait;
|
||||
|
@ -11217,9 +11301,15 @@ void Plater::reslice()
|
|||
// and notify user that he should leave it first.
|
||||
if (get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode(true))
|
||||
return;
|
||||
|
||||
// Stop arrange and (or) optimize rotation tasks.
|
||||
this->stop_jobs();
|
||||
|
||||
// Stop the running (and queued) UI jobs and only proceed if they actually
|
||||
// get stopped.
|
||||
unsigned timeout_ms = 10000;
|
||||
if (!stop_queue(this->get_ui_job_worker(), timeout_ms)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Could not stop UI job within "
|
||||
<< timeout_ms << " milliseconds timeout!";
|
||||
return;
|
||||
}
|
||||
|
||||
// Orca: regenerate CalibPressureAdvancePattern custom G-code to apply changes
|
||||
if (model().calib_pa_pattern) {
|
||||
|
@ -12077,8 +12167,10 @@ GLCanvas3D* Plater::get_current_canvas3D(bool exclude_preview)
|
|||
|
||||
void Plater::arrange()
|
||||
{
|
||||
if (!p->m_ui_jobs.is_any_running()) {
|
||||
p->m_ui_jobs.arrange();
|
||||
auto &w = get_ui_job_worker();
|
||||
if (w.is_idle()) {
|
||||
p->take_snapshot(_u8L("Arrange"));
|
||||
replace_job(w, std::make_unique<ArrangeJob>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12165,22 +12257,35 @@ void Plater::changed_mesh(int obj_idx)
|
|||
p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::changed_object(ModelObject &object){
|
||||
assert(object.get_model() == &p->model); // is object from same model?
|
||||
object.invalidate_bounding_box();
|
||||
|
||||
// recenter and re - align to Z = 0
|
||||
object.ensure_on_bed(p->printer_technology != ptSLA);
|
||||
|
||||
if (p->printer_technology == ptSLA) {
|
||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||
// pulls the correct data, update the 3D scene.
|
||||
p->update_restart_background_process(true, false);
|
||||
} else
|
||||
p->view3D->reload_scene(false);
|
||||
|
||||
// update print
|
||||
p->schedule_background_process();
|
||||
|
||||
// Check outside bed
|
||||
get_current_canvas3D()->requires_check_outside_state();
|
||||
}
|
||||
|
||||
void Plater::changed_object(int obj_idx)
|
||||
{
|
||||
if (obj_idx < 0)
|
||||
return;
|
||||
// recenter and re - align to Z = 0
|
||||
p->model.objects[obj_idx]->ensure_on_bed(p->printer_technology != ptSLA);
|
||||
if (this->p->printer_technology == ptSLA) {
|
||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||
// pulls the correct data, update the 3D scene.
|
||||
this->p->update_restart_background_process(true, false);
|
||||
}
|
||||
else
|
||||
p->view3D->reload_scene(false);
|
||||
|
||||
// update print
|
||||
this->p->schedule_background_process();
|
||||
ModelObject *object = p->model.objects[obj_idx];
|
||||
if (object == nullptr)
|
||||
return;
|
||||
changed_object(*object);
|
||||
}
|
||||
|
||||
void Plater::changed_objects(const std::vector<size_t>& object_idxs)
|
||||
|
@ -12234,7 +12339,14 @@ void Plater::center_selection() { p->center_selection(); }
|
|||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||
void Plater::split_object() { p->split_object(); }
|
||||
void Plater::split_volume() { p->split_volume(); }
|
||||
void Plater::optimize_rotation() { if (!p->m_ui_jobs.is_any_running()) p->m_ui_jobs.optimize_rotation(); }
|
||||
void Plater::optimize_rotation()
|
||||
{
|
||||
auto &w = get_ui_job_worker();
|
||||
if (w.is_idle()) {
|
||||
p->take_snapshot(_u8L("Optimize Rotation"));
|
||||
replace_job(w, std::make_unique<OrientJob>());
|
||||
}
|
||||
}
|
||||
void Plater::update_menus() { p->menus.update(); }
|
||||
// BBS
|
||||
//void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); }
|
||||
|
@ -13140,14 +13252,6 @@ void Plater::show_status_message(std::string s)
|
|||
BOOST_LOG_TRIVIAL(trace) << "show_status_message:" << s;
|
||||
}
|
||||
|
||||
void Plater::edit_text()
|
||||
{
|
||||
auto &manager = get_view3D_canvas3D()->get_gizmos_manager();
|
||||
manager.open_gizmo(GLGizmosManager::Text);
|
||||
update();
|
||||
}
|
||||
|
||||
bool Plater::can_edit_text() const { return p->can_edit_text(); }
|
||||
bool Plater::can_delete() const { return p->can_delete(); }
|
||||
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
||||
bool Plater::can_add_model() const { return !is_background_process_slicing(); }
|
||||
|
@ -13317,6 +13421,8 @@ bool Plater::PopupObjectTableBySelection()
|
|||
wxMenu* Plater::plate_menu() { return p->menus.plate_menu(); }
|
||||
wxMenu* Plater::object_menu() { return p->menus.object_menu(); }
|
||||
wxMenu* Plater::part_menu() { return p->menus.part_menu(); }
|
||||
wxMenu* Plater::text_part_menu() { return p->menus.text_part_menu(); }
|
||||
wxMenu* Plater::svg_part_menu() { return p->menus.svg_part_menu(); }
|
||||
wxMenu* Plater::sla_object_menu() { return p->menus.sla_object_menu(); }
|
||||
wxMenu* Plater::default_menu() { return p->menus.default_menu(); }
|
||||
wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); }
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "Jobs/Job.hpp"
|
||||
#include "Jobs/Worker.hpp"
|
||||
#include "Search.hpp"
|
||||
#include "PartPlate.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
@ -302,8 +303,41 @@ public:
|
|||
void update(bool conside_update_flag = false, bool force_background_processing_update = false);
|
||||
//BBS
|
||||
void object_list_changed();
|
||||
void stop_jobs();
|
||||
bool is_any_job_running() const;
|
||||
|
||||
// Get the worker handling the UI jobs (arrange, fill bed, etc...)
|
||||
// Here is an example of starting up an ad-hoc job:
|
||||
// queue_job(
|
||||
// get_ui_job_worker(),
|
||||
// [](Job::Ctl &ctl) {
|
||||
// // Executed in the worker thread
|
||||
//
|
||||
// CursorSetterRAII cursor_setter{ctl};
|
||||
// std::string msg = "Running";
|
||||
//
|
||||
// ctl.update_status(0, msg);
|
||||
// for (int i = 0; i < 100; i++) {
|
||||
// usleep(100000);
|
||||
// if (ctl.was_canceled()) break;
|
||||
// ctl.update_status(i + 1, msg);
|
||||
// }
|
||||
// ctl.update_status(100, msg);
|
||||
// },
|
||||
// [](bool, std::exception_ptr &e) {
|
||||
// // Executed in UI thread after the work is done
|
||||
//
|
||||
// try {
|
||||
// if (e) std::rethrow_exception(e);
|
||||
// } catch (std::exception &e) {
|
||||
// BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
// }
|
||||
// e = nullptr;
|
||||
// });
|
||||
// This would result in quick run of the progress indicator notification
|
||||
// from 0 to 100. Use replace_job() instead of queue_job() to cancel all
|
||||
// pending jobs.
|
||||
Worker& get_ui_job_worker();
|
||||
const Worker & get_ui_job_worker() const;
|
||||
|
||||
void select_view(const std::string& direction);
|
||||
//BBS: add no_slice logic
|
||||
void select_view_3D(const std::string& name, bool no_slice = true);
|
||||
|
@ -392,6 +426,7 @@ public:
|
|||
void clear_before_change_mesh(int obj_idx);
|
||||
void changed_mesh(int obj_idx);
|
||||
|
||||
void changed_object(ModelObject &object);
|
||||
void changed_object(int obj_idx);
|
||||
void changed_objects(const std::vector<size_t>& object_idxs);
|
||||
void schedule_background_process(bool schedule = true);
|
||||
|
@ -505,10 +540,6 @@ public:
|
|||
//BBS:
|
||||
void fill_color(int extruder_id);
|
||||
|
||||
//BBS:
|
||||
void edit_text();
|
||||
bool can_edit_text() const;
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_add_model() const;
|
||||
|
@ -724,6 +755,8 @@ public:
|
|||
wxMenu* plate_menu();
|
||||
wxMenu* object_menu();
|
||||
wxMenu* part_menu();
|
||||
wxMenu* text_part_menu();
|
||||
wxMenu* svg_part_menu();
|
||||
wxMenu* sla_object_menu();
|
||||
wxMenu* default_menu();
|
||||
wxMenu* instance_menu();
|
||||
|
@ -778,6 +811,7 @@ private:
|
|||
void cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes);
|
||||
|
||||
friend class SuppressBackgroundProcessingUpdate;
|
||||
friend class PlaterDropTarget;
|
||||
};
|
||||
|
||||
class SuppressBackgroundProcessingUpdate
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
#include "Widgets/WebView.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
|
||||
#include <wx/regex.h>
|
||||
#include <wx/progdlg.h>
|
||||
|
@ -1162,6 +1164,7 @@ InputIpAddressDialog::InputIpAddressDialog(wxWindow* parent)
|
|||
m_status_bar = std::make_shared<BBLStatusBarSend>(this);
|
||||
m_status_bar->get_panel()->Hide();
|
||||
|
||||
m_worker = std::make_unique<PlaterWorker<BoostThreadWorker>>(this, m_status_bar, "send_worker");
|
||||
|
||||
auto m_step_icon_panel1 = new wxWindow(this, wxID_ANY);
|
||||
auto m_step_icon_panel2 = new wxWindow(this, wxID_ANY);
|
||||
|
@ -1278,12 +1281,7 @@ InputIpAddressDialog::InputIpAddressDialog(wxWindow* parent)
|
|||
|
||||
void InputIpAddressDialog::on_cancel()
|
||||
{
|
||||
if (m_send_job) {
|
||||
if (m_send_job->is_running()) {
|
||||
m_send_job->cancel();
|
||||
m_send_job->join();
|
||||
}
|
||||
}
|
||||
m_worker->cancel_all();
|
||||
if (m_result == 0){
|
||||
this->EndModal(wxID_YES);
|
||||
}else {
|
||||
|
@ -1401,25 +1399,17 @@ void InputIpAddressDialog::on_ok(wxMouseEvent& evt)
|
|||
m_button_ok->SetBackgroundColor(wxColour(0x90, 0x90, 0x90));
|
||||
m_button_ok->SetBorderColor(wxColour(0x90, 0x90, 0x90));
|
||||
|
||||
if (m_send_job) {
|
||||
m_send_job->join();
|
||||
}
|
||||
m_worker->wait_for_idle();
|
||||
|
||||
m_status_bar->reset();
|
||||
m_status_bar->set_prog_block();
|
||||
m_status_bar->set_cancel_callback_fina([this]() {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled";
|
||||
if (m_send_job) {
|
||||
if (m_send_job->is_running()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: canceled";
|
||||
m_send_job->cancel();
|
||||
}
|
||||
m_send_job->join();
|
||||
}
|
||||
m_worker->cancel_all();
|
||||
});
|
||||
|
||||
|
||||
m_send_job = std::make_shared<SendJob>(m_status_bar, wxGetApp().plater(), m_obj->dev_id);
|
||||
auto m_send_job = std::make_unique<SendJob>(m_obj->dev_id);
|
||||
m_send_job->m_dev_ip = ip.ToStdString();
|
||||
m_send_job->m_access_code = str_access_code.ToStdString();
|
||||
|
||||
|
@ -1454,7 +1444,7 @@ void InputIpAddressDialog::on_ok(wxMouseEvent& evt)
|
|||
|
||||
});
|
||||
|
||||
m_send_job->start();
|
||||
replace_job(*m_worker, std::move(m_send_job));
|
||||
}
|
||||
|
||||
void InputIpAddressDialog::check_ip_address_failed(int result)
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include <wx/hashmap.h>
|
||||
#include <wx/webview.h>
|
||||
|
||||
#include "Jobs/Worker.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(EVT_SECONDARY_CHECK_CONFIRM, wxCommandEvent);
|
||||
|
@ -223,8 +225,8 @@ public:
|
|||
wxHyperlinkCtrl* m_trouble_shoot{ nullptr };
|
||||
bool m_show_access_code{ false };
|
||||
int m_result;
|
||||
std::shared_ptr<SendJob> m_send_job{nullptr};
|
||||
std::shared_ptr<BBLStatusBarSend> m_status_bar;
|
||||
std::shared_ptr<BBLStatusBarSend> m_status_bar;
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
|
||||
void on_cancel();
|
||||
void update_title(wxString title);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
#include "ConnectPrinter.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
|
@ -1247,6 +1249,8 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
|
|||
m_status_bar = std::make_shared<BBLStatusBarSend>(m_simplebook);
|
||||
m_panel_sending = m_status_bar->get_panel();
|
||||
m_simplebook->AddPage(m_panel_sending, wxEmptyString, false);
|
||||
|
||||
m_worker = std::make_unique<PlaterWorker<BoostThreadWorker>>(this, m_status_bar, "send_worker");
|
||||
|
||||
// finish mode
|
||||
m_panel_finish = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
@ -1701,9 +1705,7 @@ void SelectMachineDialog::prepare_mode(bool refresh_button)
|
|||
show_print_failed_info(false);
|
||||
|
||||
m_is_in_sending_mode = false;
|
||||
if (m_print_job) {
|
||||
m_print_job->join();
|
||||
}
|
||||
m_worker->wait_for_idle();
|
||||
|
||||
if (wxIsBusy())
|
||||
wxEndBusyCursor();
|
||||
|
@ -2206,12 +2208,7 @@ void SelectMachineDialog::on_cancel(wxCloseEvent &event)
|
|||
if (m_mapping_popup.IsShown())
|
||||
m_mapping_popup.Dismiss();
|
||||
|
||||
if (m_print_job) {
|
||||
if (m_print_job->is_running()) {
|
||||
m_print_job->cancel();
|
||||
m_print_job->join();
|
||||
}
|
||||
}
|
||||
m_worker->cancel_all();
|
||||
this->EndModal(wxID_CANCEL);
|
||||
}
|
||||
|
||||
|
@ -2664,13 +2661,7 @@ void SelectMachineDialog::on_send_print()
|
|||
m_status_bar->set_prog_block();
|
||||
m_status_bar->set_cancel_callback_fina([this]() {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled";
|
||||
if (m_print_job) {
|
||||
if (m_print_job->is_running()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: canceled";
|
||||
m_print_job->cancel();
|
||||
}
|
||||
m_print_job->join();
|
||||
}
|
||||
m_worker->cancel_all();
|
||||
m_is_canceled = true;
|
||||
wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL);
|
||||
wxQueueEvent(this, event);
|
||||
|
@ -2738,7 +2729,7 @@ void SelectMachineDialog::on_send_print()
|
|||
}
|
||||
}
|
||||
|
||||
m_print_job = std::make_shared<PrintJob>(m_status_bar, m_plater, m_printer_last_select);
|
||||
auto m_print_job = std::make_unique<PrintJob>(m_printer_last_select);
|
||||
m_print_job->m_dev_ip = obj_->dev_ip;
|
||||
m_print_job->m_ftp_folder = obj_->get_ftp_folder();
|
||||
m_print_job->m_access_code = obj_->get_access_code();
|
||||
|
@ -2824,7 +2815,7 @@ void SelectMachineDialog::on_send_print()
|
|||
if (agent)
|
||||
agent->track_update_property("dev_ota_version", obj_->get_ota_version());
|
||||
|
||||
m_print_job->start();
|
||||
replace_job(*m_worker, std::move(m_print_job));
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: start print job";
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include <wx/simplebook.h>
|
||||
#include <wx/hashmap.h>
|
||||
|
||||
#include "Jobs/Worker.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
enum PrinterState {
|
||||
|
@ -313,6 +315,7 @@ private:
|
|||
std::vector<FilamentInfo> m_filaments;
|
||||
std::vector<FilamentInfo> m_ams_mapping_result;
|
||||
std::shared_ptr<BBLStatusBarSend> m_status_bar;
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
|
||||
Slic3r::DynamicPrintConfig m_required_data_config;
|
||||
Slic3r::Model m_required_data_model;
|
||||
|
@ -377,7 +380,6 @@ protected:
|
|||
wxStaticText* m_statictext_finish{nullptr};
|
||||
TextInput* m_rename_input{nullptr};
|
||||
wxTimer* m_refresh_timer{ nullptr };
|
||||
std::shared_ptr<PrintJob> m_print_job;
|
||||
wxScrolledWindow* m_scrollable_view;
|
||||
wxScrolledWindow* m_sw_print_failed_info{nullptr};
|
||||
wxHyperlinkCtrl* m_hyperlink{nullptr};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,8 @@ namespace Slic3r {
|
|||
class Shader;
|
||||
class Model;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class ObjectID;
|
||||
class GLVolume;
|
||||
class GLArrow;
|
||||
class GLCurvedArrow;
|
||||
|
@ -66,13 +68,11 @@ private:
|
|||
struct TransformCache
|
||||
{
|
||||
Vec3d position;
|
||||
Vec3d rotation;
|
||||
Vec3d scaling_factor;
|
||||
Vec3d mirror;
|
||||
Transform3d rotation_matrix;
|
||||
Transform3d scale_matrix;
|
||||
Transform3d mirror_matrix;
|
||||
Transform3d full_matrix;
|
||||
|
||||
Geometry::Transformation transform;
|
||||
|
||||
TransformCache();
|
||||
explicit TransformCache(const Geometry::Transformation& transform);
|
||||
|
@ -86,22 +86,14 @@ private:
|
|||
VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform);
|
||||
|
||||
const Vec3d& get_volume_position() const { return m_volume.position; }
|
||||
const Vec3d& get_volume_rotation() const { return m_volume.rotation; }
|
||||
const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; }
|
||||
const Vec3d& get_volume_mirror() const { return m_volume.mirror; }
|
||||
const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; }
|
||||
const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; }
|
||||
const Transform3d& get_volume_mirror_matrix() const { return m_volume.mirror_matrix; }
|
||||
const Transform3d& get_volume_full_matrix() const { return m_volume.full_matrix; }
|
||||
const Geometry::Transformation& get_volume_transform() const { return m_volume.transform; }
|
||||
|
||||
const Vec3d& get_instance_position() const { return m_instance.position; }
|
||||
const Vec3d& get_instance_rotation() const { return m_instance.rotation; }
|
||||
const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; }
|
||||
const Vec3d& get_instance_mirror() const { return m_instance.mirror; }
|
||||
const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; }
|
||||
const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; }
|
||||
const Transform3d& get_instance_mirror_matrix() const { return m_instance.mirror_matrix; }
|
||||
const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; }
|
||||
const Geometry::Transformation &get_instance_transform() const { return m_instance.transform; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -146,6 +138,7 @@ private:
|
|||
ObjectIdxsToInstanceIdxsMap content;
|
||||
// List of ids of the volumes which are sinking when starting dragging
|
||||
std::vector<unsigned int> sinking_volumes;
|
||||
Vec3d rotation_pivot;
|
||||
};
|
||||
|
||||
// Volumes owned by GLCanvas3D.
|
||||
|
@ -162,10 +155,27 @@ private:
|
|||
Cache m_cache;
|
||||
Clipboard m_clipboard;
|
||||
std::optional<BoundingBoxf3> m_bounding_box;
|
||||
// Bounding box of a selection, with no instance scaling applied. This bounding box
|
||||
// is useful for absolute scaling of tilted objects in world coordinate space.
|
||||
// Bounding box of a single full instance selection, in world coordinates, with no instance scaling applied.
|
||||
// This bounding box is useful for absolute scaling of tilted objects in world coordinate space.
|
||||
// Modifiers are NOT taken in account
|
||||
std::optional<BoundingBoxf3> m_unscaled_instance_bounding_box;
|
||||
// Bounding box of a single full instance selection, in world coordinates.
|
||||
// Modifiers are NOT taken in account
|
||||
std::optional<BoundingBoxf3> m_scaled_instance_bounding_box;
|
||||
// Bounding box of a single full instance selection, in world coordinates, with no instance scaling applied.
|
||||
// Modifiers are taken in account
|
||||
std::optional<BoundingBoxf3> m_full_unscaled_instance_bounding_box;
|
||||
// Bounding box of a single full instance selection, in world coordinates.
|
||||
// Modifiers are taken in account
|
||||
std::optional<BoundingBoxf3> m_full_scaled_instance_bounding_box;
|
||||
// Bounding box of a single full instance selection, in local coordinates, with no instance scaling applied.
|
||||
// Modifiers are taken in account
|
||||
std::optional<BoundingBoxf3> m_full_unscaled_instance_local_bounding_box;
|
||||
// Bounding box aligned to the axis of the currently selected reference system (World/Object/Part)
|
||||
// and transform to place and orient it in world coordinates
|
||||
std::optional<std::pair<BoundingBoxf3, Transform3d>> m_bounding_box_in_current_reference_system;
|
||||
|
||||
std::optional<std::pair<Vec3d, double>> m_bounding_sphere;
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
GLModel m_vbo_sphere;
|
||||
|
@ -258,6 +268,9 @@ public:
|
|||
bool is_from_single_object() const;
|
||||
bool is_sla_compliant() const;
|
||||
bool is_instance_mode() const { return m_mode == Instance; }
|
||||
bool is_single_volume_or_modifier() const { return is_single_volume() || is_single_modifier(); }
|
||||
bool is_single_volume_instance() const { return is_single_full_instance() && m_list.size() == 1; }
|
||||
bool is_single_text() const;
|
||||
|
||||
bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); }
|
||||
// returns true if the selection contains all the given indices
|
||||
|
@ -282,28 +295,54 @@ public:
|
|||
const IndicesList& get_volume_idxs() const { return m_list; }
|
||||
const GLVolume* get_volume(unsigned int volume_idx) const;
|
||||
const GLVolume* get_first_volume() const { return get_volume(*m_list.begin()); }
|
||||
GLVolume* get_volume(unsigned int volume_idx);
|
||||
|
||||
const ObjectIdxsToInstanceIdxsMap& get_content() const { return m_cache.content; }
|
||||
|
||||
unsigned int volumes_count() const { return (unsigned int)m_list.size(); }
|
||||
const BoundingBoxf3& get_bounding_box() const;
|
||||
// Bounding box of a selection, with no instance scaling applied. This bounding box
|
||||
// is useful for absolute scaling of tilted objects in world coordinate space.
|
||||
// Bounding box of a single full instance selection, in world coordinates, with no instance scaling applied.
|
||||
// This bounding box is useful for absolute scaling of tilted objects in world coordinate space.
|
||||
// Modifiers are NOT taken in account
|
||||
const BoundingBoxf3& get_unscaled_instance_bounding_box() const;
|
||||
// Bounding box of a single full instance selection, in world coordinates.
|
||||
// Modifiers are NOT taken in account
|
||||
const BoundingBoxf3& get_scaled_instance_bounding_box() const;
|
||||
// Bounding box of a single full instance selection, in world coordinates, with no instance scaling applied.
|
||||
// Modifiers are taken in account
|
||||
const BoundingBoxf3& get_full_unscaled_instance_bounding_box() const;
|
||||
// Bounding box of a single full instance selection, in world coordinates.
|
||||
// Modifiers are taken in account
|
||||
const BoundingBoxf3& get_full_scaled_instance_bounding_box() const;
|
||||
// Bounding box of a single full instance selection, in local coordinates, with no instance scaling applied.
|
||||
// Modifiers are taken in account
|
||||
const BoundingBoxf3& get_full_unscaled_instance_local_bounding_box() const;
|
||||
// Returns the bounding box aligned to the axes of the currently selected reference system (World/Object/Part)
|
||||
// and the transform to place and orient it in world coordinates
|
||||
const std::pair<BoundingBoxf3, Transform3d>& get_bounding_box_in_current_reference_system() const;
|
||||
// Returns the bounding box aligned to the axes of the given reference system
|
||||
// and the transform to place and orient it in world coordinates
|
||||
std::pair<BoundingBoxf3, Transform3d> get_bounding_box_in_reference_system(ECoordinatesType type) const;
|
||||
|
||||
// Returns the bounding sphere: first = center, second = radius
|
||||
const std::pair<Vec3d, double> get_bounding_sphere() const;
|
||||
|
||||
void setup_cache();
|
||||
|
||||
void translate(const Vec3d& displacement, bool local = false);
|
||||
void translate(const Vec3d& displacement, TransformationType transformation_type);
|
||||
void move_to_center(const Vec3d& displacement, bool local = false);
|
||||
void rotate(const Vec3d& rotation, TransformationType transformation_type);
|
||||
void flattening_rotate(const Vec3d& normal);
|
||||
[[deprecated("Only used by GizmoObjectManipulation")]]
|
||||
void scale_legacy(const Vec3d& scale, TransformationType transformation_type);
|
||||
void scale(const Vec3d& scale, TransformationType transformation_type);
|
||||
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
void scale_to_fit_print_volume(const BuildVolume& volume);
|
||||
#else
|
||||
void scale_to_fit_print_volume(const DynamicPrintConfig& config);
|
||||
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
void mirror(Axis axis);
|
||||
void scale_and_translate(const Vec3d& scale, const Vec3d& world_translation, TransformationType transformation_type);
|
||||
void mirror(Axis axis, TransformationType transformation_type);
|
||||
|
||||
void translate(unsigned int object_idx, const Vec3d& displacement);
|
||||
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
|
||||
|
@ -327,7 +366,7 @@ public:
|
|||
|
||||
void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color, float scale) {
|
||||
m_scale_factor = scale;
|
||||
render_bounding_box(box, color);
|
||||
render_bounding_box(box, Transform3d::Identity(), color);
|
||||
}
|
||||
|
||||
//BBS
|
||||
|
@ -361,9 +400,16 @@ private:
|
|||
void do_remove_volume(unsigned int volume_idx);
|
||||
void do_remove_instance(unsigned int object_idx, unsigned int instance_idx);
|
||||
void do_remove_object(unsigned int object_idx);
|
||||
void set_bounding_boxes_dirty() { m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); }
|
||||
void set_bounding_boxes_dirty() {
|
||||
m_bounding_box.reset();
|
||||
m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset();
|
||||
m_full_unscaled_instance_bounding_box.reset(); m_full_scaled_instance_bounding_box.reset();
|
||||
m_full_unscaled_instance_local_bounding_box.reset();
|
||||
m_bounding_box_in_current_reference_system.reset();
|
||||
m_bounding_sphere.reset();
|
||||
}
|
||||
void render_synchronized_volumes();
|
||||
void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color);
|
||||
void render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const ColorRGB& color);
|
||||
void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix);
|
||||
void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix);
|
||||
//BBS: GUI refactor: add uniform_scale from gizmo
|
||||
|
@ -371,11 +417,13 @@ private:
|
|||
void render_sidebar_layers_hints(const std::string& sidebar_field, GLShaderProgram& shader);
|
||||
|
||||
public:
|
||||
enum SyncRotationType {
|
||||
enum class SyncRotationType {
|
||||
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
|
||||
SYNC_ROTATION_NONE = 0,
|
||||
NONE = 0,
|
||||
// Synchronize after rotation by an axis not parallel with Z.
|
||||
SYNC_ROTATION_GENERAL = 1,
|
||||
GENERAL = 1,
|
||||
// Synchronize after rotation reset.
|
||||
RESET = 2
|
||||
};
|
||||
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||
void synchronize_unselected_volumes();
|
||||
|
@ -387,8 +435,19 @@ private:
|
|||
|
||||
void paste_volumes_from_clipboard();
|
||||
void paste_objects_from_clipboard();
|
||||
|
||||
void transform_instance_relative(GLVolume& volume, const VolumeCache& volume_data, TransformationType transformation_type,
|
||||
const Transform3d& transform, const Vec3d& world_pivot);
|
||||
void transform_volume_relative(GLVolume& volume, const VolumeCache& volume_data, TransformationType transformation_type,
|
||||
const Transform3d& transform, const Vec3d& world_pivot);
|
||||
};
|
||||
|
||||
ModelVolume *get_selected_volume (const Selection &selection);
|
||||
const GLVolume *get_selected_gl_volume(const Selection &selection);
|
||||
|
||||
ModelVolume *get_selected_volume (const ObjectID &volume_id, const Selection &selection);
|
||||
ModelVolume *get_volume (const ObjectID &volume_id, const Selection &selection);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
#include "ConnectPrinter.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
@ -300,6 +302,8 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
|
|||
m_status_bar = std::make_shared<BBLStatusBarSend>(m_simplebook);
|
||||
m_panel_sending = m_status_bar->get_panel();
|
||||
m_simplebook->AddPage(m_panel_sending, wxEmptyString, false);
|
||||
|
||||
m_worker = std::make_unique<PlaterWorker<BoostThreadWorker>>(this, m_status_bar, "send_worker");
|
||||
|
||||
// finish mode
|
||||
m_panel_finish = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
@ -580,9 +584,7 @@ void SendToPrinterDialog::prepare_mode()
|
|||
{
|
||||
m_is_in_sending_mode = false;
|
||||
m_comboBox_printer->Enable();
|
||||
if (m_send_job) {
|
||||
m_send_job->join();
|
||||
}
|
||||
m_worker->wait_for_idle();
|
||||
|
||||
if (wxIsBusy())
|
||||
wxEndBusyCursor();
|
||||
|
@ -670,12 +672,7 @@ void SendToPrinterDialog::init_timer()
|
|||
|
||||
void SendToPrinterDialog::on_cancel(wxCloseEvent &event)
|
||||
{
|
||||
if (m_send_job) {
|
||||
if (m_send_job->is_running()) {
|
||||
m_send_job->cancel();
|
||||
m_send_job->join();
|
||||
}
|
||||
}
|
||||
m_worker->cancel_all();
|
||||
this->EndModal(wxID_CANCEL);
|
||||
}
|
||||
|
||||
|
@ -712,13 +709,7 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
|
|||
m_status_bar->set_prog_block();
|
||||
m_status_bar->set_cancel_callback_fina([this]() {
|
||||
BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled";
|
||||
if (m_send_job) {
|
||||
if (m_send_job->is_running()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: canceled";
|
||||
m_send_job->cancel();
|
||||
}
|
||||
m_send_job->join();
|
||||
}
|
||||
m_worker->cancel_all();
|
||||
m_is_canceled = true;
|
||||
wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL);
|
||||
wxQueueEvent(this, event);
|
||||
|
@ -776,7 +767,7 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
|
|||
|
||||
|
||||
|
||||
m_send_job = std::make_shared<SendJob>(m_status_bar, m_plater, m_printer_last_select);
|
||||
auto m_send_job = std::make_unique<SendJob>(m_printer_last_select);
|
||||
m_send_job->m_dev_ip = obj_->dev_ip;
|
||||
m_send_job->m_access_code = obj_->get_access_code();
|
||||
|
||||
|
@ -805,12 +796,10 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
|
|||
if (obj_->is_lan_mode_printer()) {
|
||||
m_send_job->set_check_mode();
|
||||
m_send_job->check_and_continue();
|
||||
m_send_job->start();
|
||||
}
|
||||
else {
|
||||
m_send_job->start();
|
||||
}
|
||||
|
||||
replace_job(*m_worker, std::move(m_send_job));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "send_job: send print job";
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,6 @@ private:
|
|||
wxStaticText* m_file_name;
|
||||
PrintDialogStatus m_print_status{ PrintStatusInit };
|
||||
|
||||
std::shared_ptr<SendJob> m_send_job{nullptr};
|
||||
std::vector<wxString> m_bedtype_list;
|
||||
std::map<std::string, ::CheckBox*> m_checkbox_list;
|
||||
std::vector<MachineObject*> m_list;
|
||||
|
@ -113,6 +112,7 @@ private:
|
|||
wxColour m_colour_bold_color{ wxColour(38, 46, 48) };
|
||||
wxTimer* m_refresh_timer{ nullptr };
|
||||
std::shared_ptr<BBLStatusBarSend> m_status_bar;
|
||||
std::unique_ptr<Worker> m_worker;
|
||||
wxScrolledWindow* m_sw_print_failed_info{nullptr};
|
||||
std::shared_ptr<int> m_token = std::make_shared<int>(0);
|
||||
|
||||
|
|
732
src/slic3r/GUI/SurfaceDrag.cpp
Normal file
732
src/slic3r/GUI/SurfaceDrag.cpp
Normal file
|
@ -0,0 +1,732 @@
|
|||
///|/ Copyright (c) Prusa Research 2023 Oleksandra Iushchenko @YuSanka
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "SurfaceDrag.hpp"
|
||||
|
||||
#include <libslic3r/Model.hpp> // ModelVolume
|
||||
#include <libslic3r/Emboss.hpp>
|
||||
|
||||
#include "slic3r/Utils/RaycastManager.hpp"
|
||||
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "Camera.hpp"
|
||||
#include "CameraUtils.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Gizmos/GizmoObjectManipulation.hpp"
|
||||
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
namespace{
|
||||
// Distance of embossed volume from surface to be represented as distance surface
|
||||
// Maximal distance is also enlarge by size of emboss depth
|
||||
constexpr Slic3r::MinMax<double> surface_distance_sq{1e-4, 10.}; // [in mm]
|
||||
|
||||
/// <summary>
|
||||
/// Extract position of mouse from mouse event
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Event</param>
|
||||
/// <returns>Position</returns>
|
||||
Vec2d mouse_position(const wxMouseEvent &mouse_event);
|
||||
|
||||
bool start_dragging(const Vec2d &mouse_pos,
|
||||
const Camera &camera,
|
||||
std::optional<SurfaceDrag> &surface_drag,
|
||||
GLCanvas3D &canvas,
|
||||
RaycastManager &raycast_manager,
|
||||
const std::optional<double> &up_limit);
|
||||
|
||||
bool dragging(const Vec2d &mouse_pos,
|
||||
const Camera &camera,
|
||||
SurfaceDrag &surface_drag, // need to write whether exist hit
|
||||
GLCanvas3D &canvas,
|
||||
const RaycastManager &raycast_manager,
|
||||
const std::optional<double> &up_limit);
|
||||
|
||||
Transform3d get_volume_transformation(
|
||||
Transform3d world, // from volume
|
||||
const Vec3d& world_dir, // wanted new direction
|
||||
const Vec3d& world_position, // wanted new position
|
||||
const std::optional<Transform3d>& fix, // [optional] fix matrix
|
||||
// Invers transformation of text volume instance
|
||||
// Help convert world transformation to instance space
|
||||
const Transform3d& instance_inv,
|
||||
// initial rotation in Z axis
|
||||
std::optional<float> current_angle = {},
|
||||
const std::optional<double> &up_limit = {});
|
||||
|
||||
// distinguish between transformation of volume inside object
|
||||
// and object(single full instance with one volume)
|
||||
bool is_embossed_object(const Selection &selection);
|
||||
|
||||
/// <summary>
|
||||
/// Get fix transformation for selected volume
|
||||
/// Fix after store to 3mf
|
||||
/// </summary>
|
||||
/// <param name="selection">Select only wanted volume</param>
|
||||
/// <returns>Pointer on fix transformation from ModelVolume when exists otherwise nullptr</returns>
|
||||
const Transform3d *get_fix_transformation(const Selection &selection);
|
||||
}
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
// Calculate scale in world for check in debug
|
||||
[[maybe_unused]] static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
|
||||
{
|
||||
Vec3d from_dir = from * dir;
|
||||
Vec3d to_dir = to * dir;
|
||||
double from_scale_sq = from_dir.squaredNorm();
|
||||
double to_scale_sq = to_dir.squaredNorm();
|
||||
if (is_approx(from_scale_sq, to_scale_sq, 1e-3))
|
||||
return {}; // no scale
|
||||
return sqrt(from_scale_sq / to_scale_sq);
|
||||
}
|
||||
|
||||
bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
|
||||
const Camera &camera,
|
||||
std::optional<SurfaceDrag> &surface_drag,
|
||||
GLCanvas3D &canvas,
|
||||
RaycastManager &raycast_manager,
|
||||
const std::optional<double>&up_limit)
|
||||
{
|
||||
// Fix when leave window during dragging
|
||||
// Fix when click right button
|
||||
if (surface_drag.has_value() && !mouse_event.Dragging()) {
|
||||
// write transformation from UI into model
|
||||
canvas.do_move(L("Move over surface"));
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
|
||||
// allow moving with object again
|
||||
canvas.enable_moving(true);
|
||||
canvas.enable_picking(true);
|
||||
surface_drag.reset();
|
||||
|
||||
// only left up is correct
|
||||
// otherwise it is fix state and return false
|
||||
return mouse_event.LeftUp();
|
||||
}
|
||||
|
||||
if (mouse_event.Moving())
|
||||
return false;
|
||||
|
||||
if (mouse_event.LeftDown())
|
||||
return start_dragging(mouse_position(mouse_event), camera, surface_drag, canvas, raycast_manager, up_limit);
|
||||
|
||||
// Dragging starts out of window
|
||||
if (!surface_drag.has_value())
|
||||
return false;
|
||||
|
||||
if (mouse_event.Dragging())
|
||||
return dragging(mouse_position(mouse_event), camera, *surface_drag, canvas, raycast_manager, up_limit);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager) {
|
||||
const GLVolume *gl_volume_ptr = get_selected_gl_volume(selection);
|
||||
if (gl_volume_ptr == nullptr)
|
||||
return {};
|
||||
const GLVolume& gl_volume = *gl_volume_ptr;
|
||||
|
||||
const ModelObjectPtrs &objects = selection.get_model()->objects;
|
||||
const ModelVolume* volume = get_model_volume(gl_volume, objects);
|
||||
if (volume == nullptr)
|
||||
return {};
|
||||
|
||||
const ModelInstance* instance = get_model_instance(gl_volume, objects);
|
||||
if (instance == nullptr)
|
||||
return {};
|
||||
|
||||
// Move object on surface
|
||||
auto cond = RaycastManager::SkipVolume(volume->id().id);
|
||||
raycast_manager.actualize(*instance, &cond);
|
||||
|
||||
Transform3d to_world = world_matrix_fixed(gl_volume, objects);
|
||||
Vec3d point = to_world.translation();
|
||||
Vec3d dir = -get_z_base(to_world);
|
||||
// ray in direction of text projection(from volume zero to z-dir)
|
||||
std::optional<RaycastManager::Hit> hit_opt = raycast_manager.closest_hit(point, dir, &cond);
|
||||
|
||||
// Try to find closest point when no hit object in emboss direction
|
||||
if (!hit_opt.has_value()) {
|
||||
std::optional<RaycastManager::ClosePoint> close_point_opt = raycast_manager.closest(point);
|
||||
|
||||
// It should NOT appear. Closest point always exists.
|
||||
assert(close_point_opt.has_value());
|
||||
if (!close_point_opt.has_value())
|
||||
return {};
|
||||
|
||||
// It is no neccesary to move with origin by very small value
|
||||
if (close_point_opt->squared_distance < EPSILON)
|
||||
return {};
|
||||
|
||||
const RaycastManager::ClosePoint &close_point = *close_point_opt;
|
||||
Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key);
|
||||
Vec3d hit_world = hit_tr * close_point.point;
|
||||
Vec3d offset_world = hit_world - point; // vector in world
|
||||
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
|
||||
return offset_volume;
|
||||
}
|
||||
|
||||
// It is no neccesary to move with origin by very small value
|
||||
const RaycastManager::Hit &hit = *hit_opt;
|
||||
if (hit.squared_distance < EPSILON)
|
||||
return {};
|
||||
Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key);
|
||||
Vec3d hit_world = hit_tr * hit.position;
|
||||
Vec3d offset_world = hit_world - point; // vector in world
|
||||
// TIP: It should be close to only z move
|
||||
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
|
||||
return offset_volume;
|
||||
}
|
||||
|
||||
std::optional<float> calc_distance(const GLVolume &gl_volume, RaycastManager &raycaster, GLCanvas3D &canvas)
|
||||
{
|
||||
const ModelObject *object = get_model_object(gl_volume, canvas.get_model()->objects);
|
||||
assert(object != nullptr);
|
||||
if (object == nullptr)
|
||||
return {};
|
||||
|
||||
const ModelInstance *instance = get_model_instance(gl_volume, *object);
|
||||
const ModelVolume *volume = get_model_volume(gl_volume, *object);
|
||||
assert(instance != nullptr && volume != nullptr);
|
||||
if (object == nullptr || instance == nullptr || volume == nullptr)
|
||||
return {};
|
||||
|
||||
if (volume->is_the_only_one_part())
|
||||
return {};
|
||||
|
||||
RaycastManager::AllowVolumes condition = create_condition(object->volumes, volume->id());
|
||||
RaycastManager::Meshes meshes = create_meshes(canvas, condition);
|
||||
raycaster.actualize(*instance, &condition, &meshes);
|
||||
return calc_distance(gl_volume, raycaster, &condition);
|
||||
}
|
||||
|
||||
std::optional<float> calc_distance(const GLVolume &gl_volume, const RaycastManager &raycaster, const RaycastManager::ISkip *condition)
|
||||
{
|
||||
Transform3d w = gl_volume.world_matrix();
|
||||
Vec3d p = w.translation();
|
||||
Vec3d dir = -get_z_base(w);
|
||||
auto hit_opt = raycaster.closest_hit(p, dir, condition);
|
||||
if (!hit_opt.has_value())
|
||||
return {};
|
||||
|
||||
const RaycastManager::Hit &hit = *hit_opt;
|
||||
// NOTE: hit.squared_distance is in volume space not world
|
||||
|
||||
const Transform3d &tr = raycaster.get_transformation(hit.tr_key);
|
||||
Vec3d hit_world = tr * hit.position;
|
||||
Vec3d p_to_hit = hit_world - p;
|
||||
double distance_sq = p_to_hit.squaredNorm();
|
||||
|
||||
// too small distance is calculated as zero distance
|
||||
if (distance_sq < ::surface_distance_sq.min)
|
||||
return {};
|
||||
|
||||
// check maximal distance
|
||||
const BoundingBoxf3& bb = gl_volume.bounding_box();
|
||||
double max_squared_distance = std::max(std::pow(2 * bb.size().z(), 2), ::surface_distance_sq.max);
|
||||
if (distance_sq > max_squared_distance)
|
||||
return {};
|
||||
|
||||
// calculate sign
|
||||
float sign = (p_to_hit.dot(dir) > 0)? 1.f : -1.f;
|
||||
|
||||
// distiguish sign
|
||||
return sign * static_cast<float>(sqrt(distance_sq));
|
||||
}
|
||||
|
||||
std::optional<float> calc_angle(const Selection &selection)
|
||||
{
|
||||
const GLVolume *gl_volume = selection.get_first_volume();
|
||||
assert(gl_volume != nullptr);
|
||||
if (gl_volume == nullptr)
|
||||
return {};
|
||||
|
||||
Transform3d to_world = gl_volume->world_matrix();
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
assert(volume != nullptr);
|
||||
assert(volume->emboss_shape.has_value());
|
||||
if (volume == nullptr || !volume->emboss_shape.has_value() || !volume->emboss_shape->fix_3mf_tr)
|
||||
return Emboss::calc_up(to_world, UP_LIMIT);
|
||||
|
||||
// exist fix matrix and must be applied before calculation
|
||||
to_world = to_world * volume->emboss_shape->fix_3mf_tr->inverse();
|
||||
return Emboss::calc_up(to_world, UP_LIMIT);
|
||||
}
|
||||
|
||||
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects)
|
||||
{
|
||||
Transform3d res = gl_volume.world_matrix();
|
||||
|
||||
const ModelVolume *mv = get_model_volume(gl_volume, objects);
|
||||
if (!mv)
|
||||
return res;
|
||||
|
||||
const std::optional<EmbossShape> &es = mv->emboss_shape;
|
||||
if (!es.has_value())
|
||||
return res;
|
||||
|
||||
const std::optional<Transform3d> &fix = es->fix_3mf_tr;
|
||||
if (!fix.has_value())
|
||||
return res;
|
||||
|
||||
return res * fix->inverse();
|
||||
}
|
||||
|
||||
Transform3d world_matrix_fixed(const Selection &selection)
|
||||
{
|
||||
const GLVolume *gl_volume = get_selected_gl_volume(selection);
|
||||
assert(gl_volume != nullptr);
|
||||
if (gl_volume == nullptr)
|
||||
return Transform3d::Identity();
|
||||
|
||||
return world_matrix_fixed(*gl_volume, selection.get_model()->objects);
|
||||
}
|
||||
|
||||
void selection_transform(Selection &selection, const std::function<void()> &selection_transformation_fnc)
|
||||
{
|
||||
if (const Transform3d *fix = get_fix_transformation(selection); fix != nullptr) {
|
||||
// NOTE: need editable gl volume .. can't use selection.get_first_volume()
|
||||
GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix();
|
||||
gl_volume->set_volume_transformation(volume_tr * fix->inverse());
|
||||
selection.setup_cache();
|
||||
|
||||
selection_transformation_fnc();
|
||||
|
||||
volume_tr = gl_volume->get_volume_transformation().get_matrix();
|
||||
gl_volume->set_volume_transformation(volume_tr * (*fix));
|
||||
selection.setup_cache();
|
||||
} else {
|
||||
selection_transformation_fnc();
|
||||
}
|
||||
|
||||
if (selection.is_single_full_instance())
|
||||
selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL);
|
||||
}
|
||||
|
||||
bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas, const std::optional<double> &wanted_up_limit)
|
||||
{
|
||||
GLVolume *gl_volume_ptr = get_selected_gl_volume(canvas);
|
||||
if (gl_volume_ptr == nullptr)
|
||||
return false;
|
||||
GLVolume &gl_volume = *gl_volume_ptr;
|
||||
|
||||
const ModelObjectPtrs &objects = canvas.get_model()->objects;
|
||||
ModelObject *object_ptr = get_model_object(gl_volume, objects);
|
||||
assert(object_ptr != nullptr);
|
||||
if (object_ptr == nullptr)
|
||||
return false;
|
||||
ModelObject &object = *object_ptr;
|
||||
|
||||
ModelInstance *instance_ptr = get_model_instance(gl_volume, object);
|
||||
assert(instance_ptr != nullptr);
|
||||
if (instance_ptr == nullptr)
|
||||
return false;
|
||||
ModelInstance &instance = *instance_ptr;
|
||||
|
||||
ModelVolume *volume_ptr = get_model_volume(gl_volume, object);
|
||||
assert(volume_ptr != nullptr);
|
||||
if (volume_ptr == nullptr)
|
||||
return false;
|
||||
ModelVolume &volume = *volume_ptr;
|
||||
|
||||
// Calculate new volume transformation
|
||||
Transform3d volume_tr = volume.get_matrix();
|
||||
std::optional<Transform3d> fix;
|
||||
if (volume.emboss_shape.has_value()) {
|
||||
fix = volume.emboss_shape->fix_3mf_tr;
|
||||
if (fix.has_value())
|
||||
volume_tr = volume_tr * fix->inverse();
|
||||
}
|
||||
|
||||
Transform3d instance_tr = instance.get_matrix();
|
||||
Transform3d instance_tr_inv = instance_tr.inverse();
|
||||
Transform3d world_tr = instance_tr * volume_tr; // without sla !!!
|
||||
std::optional<float> current_angle;
|
||||
if (wanted_up_limit.has_value())
|
||||
current_angle = Emboss::calc_up(world_tr, *wanted_up_limit);
|
||||
|
||||
Vec3d world_position = gl_volume.world_matrix()*Vec3d::Zero();
|
||||
|
||||
assert(camera.get_type() == Camera::EType::Perspective ||
|
||||
camera.get_type() == Camera::EType::Ortho);
|
||||
Vec3d wanted_direction = (camera.get_type() == Camera::EType::Perspective) ?
|
||||
Vec3d(camera.get_position() - world_position) :
|
||||
(-camera.get_dir_forward());
|
||||
|
||||
Transform3d new_volume_tr = get_volume_transformation(world_tr, wanted_direction, world_position,
|
||||
fix, instance_tr_inv, current_angle, wanted_up_limit);
|
||||
|
||||
Selection &selection = canvas.get_selection();
|
||||
if (is_embossed_object(selection)) {
|
||||
// transform instance instead of volume
|
||||
Transform3d new_instance_tr = instance_tr * new_volume_tr * volume.get_matrix().inverse();
|
||||
gl_volume.set_instance_transformation(new_instance_tr);
|
||||
|
||||
// set same transformation to other instances when instance is embossed object
|
||||
if (selection.is_single_full_instance())
|
||||
selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL);
|
||||
} else {
|
||||
// write result transformation
|
||||
gl_volume.set_volume_transformation(new_volume_tr);
|
||||
}
|
||||
|
||||
if (volume.type() == ModelVolumeType::MODEL_PART) {
|
||||
object.invalidate_bounding_box();
|
||||
object.ensure_on_bed();
|
||||
}
|
||||
|
||||
canvas.do_rotate(L("Face the camera"));
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
void do_local_z_rotate(GLCanvas3D &canvas, double relative_angle)
|
||||
{
|
||||
Selection &selection = canvas.get_selection();
|
||||
|
||||
assert(!selection.is_empty());
|
||||
if(selection.is_empty()) return;
|
||||
|
||||
bool is_single_volume = selection.volumes_count() == 1;
|
||||
assert(is_single_volume);
|
||||
if (!is_single_volume) return;
|
||||
|
||||
// Fix angle for mirrored volume
|
||||
bool is_mirrored = false;
|
||||
const GLVolume* gl_volume = selection.get_first_volume();
|
||||
if (gl_volume != nullptr) {
|
||||
const ModelInstance *instance = get_model_instance(*gl_volume, selection.get_model()->objects);
|
||||
bool is_instance_mirrored = (instance != nullptr)? has_reflection(instance->get_matrix()) : false;
|
||||
if (is_embossed_object(selection)) {
|
||||
is_mirrored = is_instance_mirrored;
|
||||
} else {
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
if (volume != nullptr)
|
||||
is_mirrored = is_instance_mirrored != has_reflection(volume->get_matrix());
|
||||
}
|
||||
}
|
||||
if (is_mirrored)
|
||||
relative_angle *= -1;
|
||||
|
||||
selection.setup_cache();
|
||||
auto selection_rotate_fnc = [&selection, &relative_angle](){
|
||||
selection.rotate(Vec3d(0., 0., relative_angle), get_drag_transformation_type(selection));
|
||||
};
|
||||
selection_transform(selection, selection_rotate_fnc);
|
||||
|
||||
std::string snapshot_name; // empty meand no store undo / redo
|
||||
// NOTE: it use L instead of _L macro because prefix _ is appended
|
||||
// inside function do_move
|
||||
// snapshot_name = L("Set text rotation");
|
||||
canvas.do_rotate(snapshot_name);
|
||||
}
|
||||
|
||||
void do_local_z_move(GLCanvas3D &canvas, double relative_move) {
|
||||
|
||||
Selection &selection = canvas.get_selection();
|
||||
assert(!selection.is_empty());
|
||||
if (selection.is_empty()) return;
|
||||
|
||||
selection.setup_cache();
|
||||
auto selection_translate_fnc = [&selection, relative_move]() {
|
||||
Vec3d translate = Vec3d::UnitZ() * relative_move;
|
||||
selection.translate(translate, TransformationType::Local);
|
||||
};
|
||||
selection_transform(selection, selection_translate_fnc);
|
||||
|
||||
std::string snapshot_name; // empty mean no store undo / redo
|
||||
// NOTE: it use L instead of _L macro because prefix _ is appended inside
|
||||
// function do_move
|
||||
// snapshot_name = L("Set surface distance");
|
||||
canvas.do_move(snapshot_name);
|
||||
}
|
||||
|
||||
TransformationType get_drag_transformation_type(const Selection &selection)
|
||||
{
|
||||
return is_embossed_object(selection) ?
|
||||
TransformationType::Instance_Relative_Joint :
|
||||
TransformationType::Local_Relative_Joint;
|
||||
}
|
||||
|
||||
void dragging_rotate_gizmo(double gizmo_angle, std::optional<float>& current_angle, std::optional<float> &start_angle, Selection &selection)
|
||||
{
|
||||
if (!start_angle.has_value())
|
||||
// create cache for initial angle
|
||||
start_angle = current_angle.value_or(0.f);
|
||||
|
||||
gizmo_angle -= PI / 2; // Grabber is upward
|
||||
|
||||
double new_angle = gizmo_angle + *start_angle;
|
||||
|
||||
const GLVolume *gl_volume = selection.get_first_volume();
|
||||
assert(gl_volume != nullptr);
|
||||
if (gl_volume == nullptr)
|
||||
return;
|
||||
|
||||
bool is_volume_mirrored = has_reflection(gl_volume->get_volume_transformation().get_matrix());
|
||||
bool is_instance_mirrored = has_reflection(gl_volume->get_instance_transformation().get_matrix());
|
||||
if (is_volume_mirrored != is_instance_mirrored)
|
||||
new_angle = -gizmo_angle + *start_angle;
|
||||
|
||||
// move to range <-M_PI, M_PI>
|
||||
Geometry::to_range_pi_pi(new_angle);
|
||||
|
||||
const Transform3d* fix = get_fix_transformation(selection);
|
||||
double z_rotation = (fix!=nullptr) ? (new_angle - current_angle.value_or(0.f)) : // relative angle
|
||||
gizmo_angle; // relativity is keep by selection cache
|
||||
|
||||
auto selection_rotate_fnc = [z_rotation, &selection]() {
|
||||
selection.rotate(Vec3d(0., 0., z_rotation), get_drag_transformation_type(selection));
|
||||
};
|
||||
selection_transform(selection, selection_rotate_fnc);
|
||||
|
||||
// propagate angle into property
|
||||
current_angle = static_cast<float>(new_angle);
|
||||
|
||||
// do not store zero
|
||||
if (is_approx(*current_angle, 0.f))
|
||||
current_angle.reset();
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
// private implementation
|
||||
namespace {
|
||||
|
||||
Vec2d mouse_position(const wxMouseEvent &mouse_event){
|
||||
// wxCoord == int --> wx/types.h
|
||||
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
|
||||
return mouse_coord.cast<double>();
|
||||
}
|
||||
|
||||
bool start_dragging(const Vec2d &mouse_pos,
|
||||
const Camera &camera,
|
||||
std::optional<SurfaceDrag> &surface_drag,
|
||||
GLCanvas3D &canvas,
|
||||
RaycastManager &raycast_manager,
|
||||
const std::optional<double>&up_limit)
|
||||
{
|
||||
// selected volume
|
||||
GLVolume *gl_volume_ptr = get_selected_gl_volume(canvas);
|
||||
if (gl_volume_ptr == nullptr)
|
||||
return false;
|
||||
const GLVolume &gl_volume = *gl_volume_ptr;
|
||||
|
||||
// is selected volume closest hovered?
|
||||
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
|
||||
if (int hovered_idx = canvas.get_first_hover_volume_idx(); hovered_idx < 0)
|
||||
return false;
|
||||
else if (auto hovered_idx_ = static_cast<size_t>(hovered_idx);
|
||||
hovered_idx_ >= gl_volumes.size() || gl_volumes[hovered_idx_] != gl_volume_ptr)
|
||||
return false;
|
||||
|
||||
const ModelObjectPtrs &objects = canvas.get_model()->objects;
|
||||
const ModelObject *object = get_model_object(gl_volume, objects);
|
||||
assert(object != nullptr);
|
||||
if (object == nullptr)
|
||||
return false;
|
||||
|
||||
const ModelInstance *instance = get_model_instance(gl_volume, *object);
|
||||
const ModelVolume *volume = get_model_volume(gl_volume, *object);
|
||||
assert(instance != nullptr && volume != nullptr);
|
||||
if (object == nullptr || instance == nullptr || volume == nullptr)
|
||||
return false;
|
||||
|
||||
// allowed drag&drop by canvas for object
|
||||
if (volume->is_the_only_one_part())
|
||||
return false;
|
||||
|
||||
RaycastManager::AllowVolumes condition = create_condition(object->volumes, volume->id());
|
||||
RaycastManager::Meshes meshes = create_meshes(canvas, condition);
|
||||
// initialize raycasters
|
||||
// INFO: It could slows down for big objects
|
||||
// (may be move to thread and do not show drag until it finish)
|
||||
raycast_manager.actualize(*instance, &condition, &meshes);
|
||||
|
||||
// world_matrix_fixed() without sla shift
|
||||
Transform3d to_world = world_matrix_fixed(gl_volume, objects);
|
||||
|
||||
// zero point of volume in world coordinate system
|
||||
Vec3d volume_center = to_world.translation();
|
||||
// screen coordinate of volume center
|
||||
Vec2i coor = CameraUtils::project(camera, volume_center);
|
||||
Vec2d mouse_offset = coor.cast<double>() - mouse_pos;
|
||||
Vec2d mouse_offset_without_sla_shift = mouse_offset;
|
||||
if (double sla_shift = gl_volume.get_sla_shift_z(); !is_approx(sla_shift, 0.)) {
|
||||
Transform3d to_world_without_sla_move = instance->get_matrix() * volume->get_matrix();
|
||||
if (volume->emboss_shape.has_value() && volume->emboss_shape->fix_3mf_tr.has_value())
|
||||
to_world_without_sla_move = to_world_without_sla_move * (*volume->emboss_shape->fix_3mf_tr);
|
||||
// zero point of volume in world coordinate system
|
||||
volume_center = to_world_without_sla_move.translation();
|
||||
// screen coordinate of volume center
|
||||
coor = CameraUtils::project(camera, volume_center);
|
||||
mouse_offset_without_sla_shift = coor.cast<double>() - mouse_pos;
|
||||
}
|
||||
|
||||
Transform3d volume_tr = gl_volume.get_volume_transformation().get_matrix();
|
||||
|
||||
// fix baked transformation from .3mf store process
|
||||
if (const std::optional<EmbossShape> &es_opt = volume->emboss_shape; es_opt.has_value()) {
|
||||
const std::optional<Slic3r::Transform3d> &fix = es_opt->fix_3mf_tr;
|
||||
if (fix.has_value())
|
||||
volume_tr = volume_tr * fix->inverse();
|
||||
}
|
||||
|
||||
Transform3d instance_tr = instance->get_matrix();
|
||||
Transform3d instance_tr_inv = instance_tr.inverse();
|
||||
Transform3d world_tr = instance_tr * volume_tr;
|
||||
std::optional<float> start_angle;
|
||||
if (up_limit.has_value()) {
|
||||
start_angle = Emboss::calc_up(world_tr, *up_limit);
|
||||
if (start_angle.has_value() && has_reflection(world_tr))
|
||||
start_angle = -(*start_angle);
|
||||
}
|
||||
|
||||
std::optional<float> start_distance;
|
||||
if (!volume->emboss_shape->projection.use_surface)
|
||||
start_distance = calc_distance(gl_volume, raycast_manager, &condition);
|
||||
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv,
|
||||
gl_volume_ptr, condition, start_angle,
|
||||
start_distance, true, mouse_offset_without_sla_shift};
|
||||
|
||||
// disable moving with object by mouse
|
||||
canvas.enable_moving(false);
|
||||
canvas.enable_picking(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
Transform3d get_volume_transformation(
|
||||
Transform3d world, // from volume
|
||||
const Vec3d& world_dir, // wanted new direction
|
||||
const Vec3d& world_position, // wanted new position
|
||||
const std::optional<Transform3d>& fix, // [optional] fix matrix
|
||||
// Invers transformation of text volume instance
|
||||
// Help convert world transformation to instance space
|
||||
const Transform3d& instance_inv,
|
||||
// initial rotation in Z axis
|
||||
std::optional<float> current_angle,
|
||||
const std::optional<double> &up_limit)
|
||||
{
|
||||
auto world_linear = world.linear();
|
||||
// Calculate offset: transformation to wanted position
|
||||
{
|
||||
// Reset skew of the text Z axis:
|
||||
// Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane.
|
||||
Vec3d old_z = world_linear.col(2);
|
||||
Vec3d new_z = world_linear.col(0).cross(world_linear.col(1));
|
||||
world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm());
|
||||
}
|
||||
|
||||
Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ()
|
||||
auto z_rotation = Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(text_z_world, world_dir);
|
||||
Transform3d world_new = z_rotation * world;
|
||||
auto world_new_linear = world_new.linear();
|
||||
|
||||
// Fix direction of up vector to zero initial rotation
|
||||
if(up_limit.has_value()){
|
||||
Vec3d z_world = world_new_linear.col(2);
|
||||
z_world.normalize();
|
||||
Vec3d wanted_up = Emboss::suggest_up(z_world, *up_limit);
|
||||
|
||||
Vec3d y_world = world_new_linear.col(1);
|
||||
auto y_rotation = Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(y_world, wanted_up);
|
||||
|
||||
world_new = y_rotation * world_new;
|
||||
world_new_linear = world_new.linear();
|
||||
}
|
||||
|
||||
// Edit position from right
|
||||
Transform3d volume_new{Eigen::Translation<double, 3>(instance_inv * world_position)};
|
||||
volume_new.linear() = instance_inv.linear() * world_new_linear;
|
||||
|
||||
// Check that transformation matrix is valid transformation
|
||||
assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN
|
||||
if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0))
|
||||
return Transform3d::Identity();
|
||||
|
||||
// Check that scale in world did not changed
|
||||
assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value());
|
||||
assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value());
|
||||
|
||||
// fix baked transformation from .3mf store process
|
||||
if (fix.has_value())
|
||||
volume_new = volume_new * (*fix);
|
||||
|
||||
// apply move in Z direction and rotation by up vector
|
||||
Emboss::apply_transformation(current_angle, {}, volume_new);
|
||||
|
||||
return volume_new;
|
||||
}
|
||||
|
||||
bool dragging(const Vec2d &mouse_pos,
|
||||
const Camera &camera,
|
||||
SurfaceDrag &surface_drag,
|
||||
GLCanvas3D &canvas,
|
||||
const RaycastManager &raycast_manager,
|
||||
const std::optional<double> &up_limit)
|
||||
{
|
||||
Vec2d offseted_mouse = mouse_pos + surface_drag.mouse_offset_without_sla_shift;
|
||||
std::optional<RaycastManager::Hit> hit = ray_from_camera(
|
||||
raycast_manager, offseted_mouse, camera, &surface_drag.condition);
|
||||
|
||||
surface_drag.exist_hit = hit.has_value();
|
||||
if (!hit.has_value()) {
|
||||
// cross hair need redraw
|
||||
canvas.set_as_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
const ModelVolume *volume = get_model_volume(*surface_drag.gl_volume, canvas.get_model()->objects);
|
||||
std::optional<Transform3d> fix;
|
||||
if (volume !=nullptr &&
|
||||
volume->emboss_shape.has_value() &&
|
||||
volume->emboss_shape->fix_3mf_tr.has_value())
|
||||
fix = volume->emboss_shape->fix_3mf_tr;
|
||||
Transform3d volume_new = get_volume_transformation(surface_drag.world, hit->normal, hit->position,
|
||||
fix, surface_drag.instance_inv, surface_drag.start_angle, up_limit);
|
||||
|
||||
// Update transformation for all instances
|
||||
for (GLVolume *vol : canvas.get_volumes().volumes) {
|
||||
if (vol->object_idx() != surface_drag.gl_volume->object_idx() ||
|
||||
vol->volume_idx() != surface_drag.gl_volume->volume_idx())
|
||||
continue;
|
||||
vol->set_volume_transformation(volume_new);
|
||||
}
|
||||
|
||||
canvas.set_as_dirty();
|
||||
// Show current position in manipulation panel
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_embossed_object(const Selection &selection)
|
||||
{
|
||||
assert(selection.volumes_count() == 1);
|
||||
return selection.is_single_full_object() || selection.is_single_full_instance();
|
||||
}
|
||||
|
||||
const Transform3d *get_fix_transformation(const Selection &selection) {
|
||||
const GLVolume *gl_volume = get_selected_gl_volume(selection);
|
||||
assert(gl_volume != nullptr);
|
||||
if (gl_volume == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
assert(volume != nullptr);
|
||||
if (volume == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const std::optional<EmbossShape> &es = volume->emboss_shape;
|
||||
if (!volume->emboss_shape.has_value())
|
||||
return nullptr;
|
||||
if (!es->fix_3mf_tr.has_value())
|
||||
return nullptr;
|
||||
return &(*es->fix_3mf_tr);
|
||||
}
|
||||
|
||||
} // namespace
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue