diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
index 56f5e433d7..dc94546859 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
@@ -14,7 +14,6 @@
#include "slic3r/GUI/Jobs/NotificationProgressIndicator.hpp"
#include "slic3r/Utils/WxFontUtils.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
-#include "slic3r/Utils/EmbossGui.hpp"
// TODO: remove include
#include "libslic3r/SVG.hpp" // debug store
@@ -108,14 +107,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
// Private namespace with helper function for create volume
namespace priv {
-
-///
-/// Check if volume type is possible use for new text volume
-///
-/// Type
-/// True when allowed otherwise false
-static bool is_valid(ModelVolumeType volume_type);
-
///
/// Prepare data for emboss
///
@@ -134,7 +125,7 @@ struct TextDataBase : public DataBase
TextDataBase(DataBase &&parent, const FontFileWithCache &font_file, TextConfiguration &&text_configuration);
// Create shape from text + font configuration
EmbossShape &create_shape() override;
- void write(ModelVolume &volume) const override;
+ void write(ModelVolume &volume) const override;
// private:
// Keep pointer on Data of font (glyph shapes)
@@ -143,31 +134,6 @@ struct TextDataBase : public DataBase
TextConfiguration text_configuration;
};
-///
-/// Start job for add object with text into scene
-///
-/// Define params of text
-/// Screen coordinat, where to create new object laying on bed
-static void start_create_object_job(std::unique_ptr emboss_data, const Vec2d &coor);
-
-///
-/// Start job for add new volume on surface of object defined by screen coor
-///
-/// Define params of text
-/// Emboss / engrave
-/// Mouse position which define position
-/// Volume to find surface for create
-/// Ability to ray cast to model
-/// Contain already used scene RayCasters
-/// True when start creation, False when there is no hit surface by screen coor
-static bool start_create_volume_on_surface_job(const FontProp &fp,
- std::unique_ptr emboss_data,
- ModelVolumeType volume_type,
- const Vec2d &screen_coor,
- const GLVolume &gl_volume,
- RaycastManager &raycaster,
- GLCanvas3D &canvas);
-
// Loaded icons enum
// Have to match order of files in function GLGizmoEmboss::init_icons()
enum class IconType : unsigned {
@@ -194,69 +160,26 @@ const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType typ
static bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false);
} // namespace priv
-void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
+bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
{
- if (!priv::is_valid(volume_type)) return;
m_style_manager.discard_style_changes();
- set_default_text();
-
- std::unique_ptr emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
- GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent);
- if (gl_volume == nullptr)
- // object is not under mouse position soo create object on plater
- return priv::start_create_object_job(std::move(emboss_data), mouse_pos);
-
+ set_default_text();
+ DataBasePtr base = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
+ Plater *plater_ptr = wxGetApp().plater();
const FontProp &fp = m_style_manager.get_style().prop;
- if (!priv::start_create_volume_on_surface_job(fp, std::move(emboss_data), volume_type, mouse_pos, *gl_volume, m_raycast_manager, m_parent))
- return create_volume(volume_type);
+ return start_create_volume(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Emboss, mouse_pos, fp.distance, fp.angle);
}
// Designed for create volume without information of mouse in scene
-void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
+bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
{
- if (!priv::is_valid(volume_type)) return;
m_style_manager.discard_style_changes();
set_default_text();
-
- // select position by camera position and view direction
- const Selection &selection = m_parent.get_selection();
- int object_idx = selection.get_object_idx();
-
- Size s = m_parent.get_canvas_size();
- Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
std::unique_ptr emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
- const ModelObjectPtrs &objects = selection.get_model()->objects;
- // No selected object so create new object
- if (selection.is_empty() || object_idx < 0 || static_cast(object_idx) >= objects.size()) {
- // create Object on center of screen
- // when ray throw center of screen not hit bed it create object on center of bed
- priv::start_create_object_job(std::move(emboss_data), screen_center);
- return;
- }
-
- // create volume inside of selected object
- Vec2d coor;
- const Camera &camera = wxGetApp().plater()->get_camera();
- const GLVolume *gl_volume = find_closest(selection, screen_center, camera, objects, &coor);
-
- FontProp &fp = m_style_manager.get_style().prop;
- if (gl_volume == nullptr) {
- return priv::start_create_object_job(std::move(emboss_data), screen_center);
- } else if (!priv::start_create_volume_on_surface_job(fp, std::move(emboss_data), volume_type, coor, *gl_volume, m_raycast_manager, m_parent)){
- // In centroid of convex hull is not hit with object. e.g. torid
- // soo create transfomation on border of object
-
- // unique pointer is already destoyed need to create again
- std::unique_ptr emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
- // there is no point on surface so no use of surface will be applied
- if (emboss_data->shape.use_surface)
- emboss_data->shape.use_surface = false;
-
- const ModelObject *object = get_model_object(*gl_volume, objects);
- Worker &worker = wxGetApp().plater()->get_ui_job_worker();
- Transform3d volume_trmat = create_volume_transformation(*gl_volume, objects, fp.size_in_mm, fp.emboss);
- start_create_volume_job(worker, *object, volume_trmat, std::move(emboss_data), volume_type);
- }
+ Plater *plater_ptr = wxGetApp().plater();
+ const FontProp &fp = m_style_manager.get_style().prop;
+ return start_create_volume_without_position(plater_ptr, std::move(emboss_data),
+ volume_type, m_raycast_manager, GLGizmosManager::Emboss, fp.distance, fp.angle);
}
bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
@@ -1057,7 +980,7 @@ bool GLGizmoEmboss::process()
if (use_surface) {
// Model to cut surface from.
- SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume);
+ SurfaceVolumeData::ModelSources sources = create_volume_sources(*m_volume);
if (sources.empty()) return false;
Transform3d text_tr = m_volume->get_matrix();
@@ -3254,17 +3177,6 @@ bool GLGizmoEmboss::is_text_object(const ModelVolume *text) {
// priv namespace implementation
///////////////
-bool priv::is_valid(ModelVolumeType volume_type)
-{
- if (volume_type == ModelVolumeType::MODEL_PART ||
- volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
- volume_type == ModelVolumeType::PARAMETER_MODIFIER)
- return true;
-
- BOOST_LOG_TRIVIAL(error) << "Can't create embossed text with this type: " << (int) volume_type;
- return false;
-}
-
priv::TextDataBase::TextDataBase(DataBase &&parent, const FontFileWithCache &font_file, TextConfiguration &&text_configuration)
: DataBase(std::move(parent)), font_file(font_file) /* copy */, text_configuration(std::move(text_configuration))
{
@@ -3351,50 +3263,6 @@ std::unique_ptr priv::create_emboss_data_base(const std::string &text,
return std::make_unique(std::move(base), font, std::move(tc));
}
-void priv::start_create_object_job(std::unique_ptr emboss_data, const Vec2d &coor) {
- Plater *plater = wxGetApp().plater();
- const Camera &camera = plater->get_camera();
- const Pointfs &bed_shape = plater->build_volume().bed_shape();
- Worker &worker = plater->get_ui_job_worker();
- DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape};
- auto job = std::make_unique(std::move(data));
- queue_job(worker, std::move(job));
-}
-
-bool priv::start_create_volume_on_surface_job(const FontProp &fp,
- std::unique_ptr emboss_data,
- ModelVolumeType volume_type,
- const Vec2d &screen_coor,
- const GLVolume &gl_volume,
- RaycastManager &raycaster,
- GLCanvas3D &canvas)
-{
- const ModelObjectPtrs &objects = canvas.get_model()->objects;
- const ModelVolume* volume = get_model_volume(gl_volume, objects);
- const ModelInstance *instance = get_model_instance(gl_volume, objects);
- if (volume == nullptr || instance == nullptr ||
- volume->get_object() == nullptr) {
- // weird situation
- assert(false);
- return false;
- }
-
- Plater* plater = wxGetApp().plater();
- const Camera &camera = plater->get_camera();
- std::optional transform = create_volume_transformation_on_surface(
- screen_coor, camera, *volume, *instance, raycaster, canvas, fp.distance, fp.angle);
-
- if (!transform.has_value()) {
- // When model is broken. It could appear that hit miss the object.
- // So add part near by in simmilar manner as right panel do
- return false;
- }
-
- // Try to cast ray into scene and find object for add volume
- Worker &worker = plater->get_ui_job_worker();
- return start_create_volume_job(worker, *volume->get_object(), *transform, std::move(emboss_data), volume_type);
-}
-
ImVec2 priv::calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size)
{
const Selection::IndicesList indices = selection.get_volume_idxs();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
index bf78fc3eb6..3b9b167ef8 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
@@ -39,13 +39,13 @@ public:
///
/// Object part / Negative volume / Modifier
/// Define position of new volume
- void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
+ bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
///
/// Create new text without given position
///
/// Object part / Negative volume / Modifier
- void create_volume(ModelVolumeType volume_type);
+ bool create_volume(ModelVolumeType volume_type);
protected:
bool on_init() override;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
index 30a3540f3d..c8eb662b55 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
@@ -9,6 +9,7 @@
#include "slic3r/GUI/MsgDialog.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
+#include "slic3r/GUI/Jobs/EmbossJob.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/Point.hpp"
@@ -32,11 +33,11 @@
using namespace Slic3r;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
+using namespace Slic3r::GUI::Emboss;
namespace priv {
// Variable keep limits for variables
-static const struct Limits
-{
+static const struct Limits{
MinMax emboss{0.01f, 1e4f}; // in mm
// distance text object from surface
MinMax angle{-180.f, 180.f}; // in degrees
@@ -55,20 +56,34 @@ GLGizmoSVG::GLGizmoSVG(GLCanvas3D &parent)
// Private functions to create emboss volume
namespace priv {
-
-///
-/// Check if volume type is possible use for new text volume
-///
-/// Type
-/// True when allowed otherwise false
-static bool is_valid(ModelVolumeType volume_type);
-
///
/// Open file dialog with svg files
///
/// File path to svg
static std::string choose_svg_file();
+///
+/// Let user to choose file with (S)calable (V)ector (G)raphics - SVG.
+/// Than let select contour
+///
+/// EmbossShape to create
+static EmbossShape select_shape();
+
+///
+/// Create new embos data
+///
+/// Cancel for previous job
+/// Base data for emboss SVG
+static DataBasePtr create_emboss_data_base(std::shared_ptr> &cancel);
+
+///
+/// Create symbol '?' as default shape
+/// without source file
+/// with size 2cm
+///
+/// Default shape to emboss
+static ExPolygons default_shape();
+
///
/// Separate file name from file path.
/// String after last delimiter and before last point
@@ -77,53 +92,39 @@ static std::string choose_svg_file();
/// File name without directory path
static std::string get_file_name(const std::string &file_path);
+///
+/// Create volume name from shape information
+///
+/// File path
+/// Name for volume
+static std::string volume_name(const EmbossShape& shape);
} // namespace priv
-void GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos){
- std::string path = priv::choose_svg_file();
- if (path.empty()) return;
- create_volume(path, volume_type, mouse_pos);
-}
-void GLGizmoSVG::create_volume(ModelVolumeType volume_type) {
- std::string path = priv::choose_svg_file();
- if (path.empty()) return;
- create_volume(path, volume_type);
+bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
+{
+ DataBasePtr base = priv::create_emboss_data_base(m_job_cancel);
+ Plater *plater_ptr = wxGetApp().plater();
+ return start_create_volume(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Svg, mouse_pos);
}
-void GLGizmoSVG::create_volume(const std::string &svg_file_path, ModelVolumeType volume_type, const Vec2d &mouse_pos) {
- std::string name = priv::get_file_name(svg_file_path);
- NSVGimage *image = nsvgParseFromFile(svg_file_path.c_str(), "mm", 96.0f);
- ExPolygons polys = NSVGUtils::to_ExPolygons(image);
- nsvgDelete(image);
-
- BoundingBox bb;
- for (const auto &p : polys)
- bb.merge(p.contour.points);
-
- double scale = 1e-4;
- auto project = std::make_unique(std::make_unique(10 / scale), scale);
- indexed_triangle_set its = polygons2model(polys, *project);
- // add volume
+bool GLGizmoSVG::create_volume(ModelVolumeType volume_type)
+{
+ DataBasePtr base = priv::create_emboss_data_base(m_job_cancel);
+ Plater *plater_ptr = wxGetApp().plater();
+ return start_create_volume_without_position(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Svg);
}
-void GLGizmoSVG::create_volume(const std::string &svg_file_path, ModelVolumeType volume_type) {
-
+bool GLGizmoSVG::is_svg(const ModelVolume &volume) {
+ return volume.emboss_shape.has_value();
}
-bool GLGizmoSVG::is_svg(const ModelVolume *volume) {
- if (volume == nullptr)
- return false;
- return volume->emboss_shape.has_value();
-}
-
-bool GLGizmoSVG::is_svg_object(const ModelVolume *volume) {
- if (volume == nullptr) return false;
- if (!volume->emboss_shape.has_value()) return false;
- if (volume->type() != ModelVolumeType::MODEL_PART) return false;
- for (const ModelVolume *v : volume->get_object()->volumes) {
- if (v == volume) continue;
+bool GLGizmoSVG::is_svg_object(const ModelVolume &volume) {
+ if (!volume.emboss_shape.has_value()) return false;
+ if (volume.type() != ModelVolumeType::MODEL_PART) return false;
+ for (const ModelVolume *v : volume.get_object()->volumes) {
+ if (v->id() == volume.id()) continue;
if (v->type() == ModelVolumeType::MODEL_PART) return false;
}
return true;
@@ -423,7 +424,7 @@ void GLGizmoSVG::set_volume_by_selection()
ImGuiWrapper::left_inputs();
// is valid svg volume?
- if (!is_svg(volume))
+ if (!is_svg(*volume))
return reset_volume();
// cancel previous job
@@ -527,8 +528,8 @@ void GLGizmoSVG::close()
{
// close gizmo == open it again
auto& mng = m_parent.get_gizmos_manager();
- if (mng.get_current_type() == GLGizmosManager::Emboss)
- mng.open_gizmo(GLGizmosManager::Emboss);
+ if (mng.get_current_type() == GLGizmosManager::Svg)
+ mng.open_gizmo(GLGizmosManager::Svg);
}
void GLGizmoSVG::draw_window()
@@ -543,7 +544,8 @@ void GLGizmoSVG::draw_window()
void GLGizmoSVG::draw_model_type()
{
- bool is_last_solid_part = is_svg_object(m_volume);
+ assert(m_volume != nullptr);
+ bool is_last_solid_part = is_svg_object(*m_volume);
std::string title = _u8L("SVG is to object");
if (is_last_solid_part) {
ImVec4 color{.5f, .5f, .5f, 1.f};
@@ -561,7 +563,7 @@ void GLGizmoSVG::draw_model_type()
if (ImGui::RadioButton(_u8L("Added").c_str(), type == part))
new_type = part;
else if (ImGui::IsItemHovered())
- ImGui::SetTooltip("%s", _u8L("Click to change text into object part.").c_str());
+ ImGui::SetTooltip("%s", _u8L("Change to object's part.").c_str());
ImGui::SameLine();
std::string last_solid_part_hint = _u8L("You can't change a type of the last solid part of the object.");
@@ -571,11 +573,16 @@ void GLGizmoSVG::draw_model_type()
if (is_last_solid_part)
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
else if (type != negative)
- ImGui::SetTooltip("%s", _u8L("Click to change part type into negative volume.").c_str());
+ ImGui::SetTooltip("%s", _u8L("Change to negative volume.").c_str());
}
- // In simple mode are not modifiers
- if (wxGetApp().plater()->printer_technology() != ptSLA && wxGetApp().get_mode() != ConfigOptionMode::comSimple) {
+ GUI_App &app = wxGetApp();
+ Plater *plater = app.plater();
+ // SLA printers do not use modifiers
+ bool is_sla = plater->printer_technology() == ptSLA;
+ // In simple mode are not modifiers ?!?
+ bool is_simple = app.get_mode() == ConfigOptionMode::comSimple;
+ if (!is_sla && !is_simple) {
ImGui::SameLine();
if (ImGui::RadioButton(_u8L("Modifier").c_str(), type == modifier))
new_type = modifier;
@@ -583,13 +590,11 @@ void GLGizmoSVG::draw_model_type()
if (is_last_solid_part)
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
else if (type != modifier)
- ImGui::SetTooltip("%s", _u8L("Click to change part type into modifier.").c_str());
+ ImGui::SetTooltip("%s", _u8L("Change to modifier.").c_str());
}
}
if (m_volume != nullptr && new_type.has_value() && !is_last_solid_part) {
- GUI_App &app = wxGetApp();
- Plater * plater = app.plater();
Plater::TakeSnapshot snapshot(plater, _L("Change Text Type"), UndoRedo::SnapshotType::GizmoAction);
m_volume->set_type(*new_type);
@@ -612,8 +617,8 @@ void GLGizmoSVG::draw_model_type()
// NOTE: on linux, function reorder_volumes_and_get_selection call GLCanvas3D::reload_scene(refresh_immediately = false)
// which discard m_volume pointer and set it to nullptr also selection is cleared so gizmo is automaticaly closed
auto &mng = m_parent.get_gizmos_manager();
- if (mng.get_current_type() != GLGizmosManager::Emboss)
- mng.open_gizmo(GLGizmosManager::Emboss);
+ if (mng.get_current_type() != GLGizmosManager::Svg)
+ mng.open_gizmo(GLGizmosManager::Svg);
// TODO: select volume back - Ask @Sasa
}
}
@@ -623,17 +628,6 @@ void GLGizmoSVG::draw_model_type()
// priv namespace implementation
///////////////
-bool priv::is_valid(ModelVolumeType volume_type)
-{
- if (volume_type == ModelVolumeType::MODEL_PART ||
- volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
- volume_type == ModelVolumeType::PARAMETER_MODIFIER )
- return true;
-
- BOOST_LOG_TRIVIAL(error) << "Can't create embossed SVG with this type: " << (int) volume_type;
- return false;
-}
-
std::string priv::get_file_name(const std::string &file_path)
{
if (file_path.empty())
@@ -660,6 +654,14 @@ std::string priv::get_file_name(const std::string &file_path)
return file_path.substr(offset, count);
}
+std::string priv::volume_name(const EmbossShape &shape)
+{
+ std::string file_name = get_file_name(shape.svg_file_path);
+ if (!file_name.empty())
+ return file_name;
+ return "SVG shape";
+}
+
std::string priv::choose_svg_file()
{
wxWindow* parent = nullptr;
@@ -689,5 +691,57 @@ std::string priv::choose_svg_file()
return path;
}
+ExPolygons priv::default_shape() {
+ std::string file = Slic3r::resources_dir() + "/icons/question.svg";
+ NSVGimage *image = nsvgParseFromFile(file.c_str(), "px", 96.0f);
+ ExPolygons shape = NSVGUtils::to_ExPolygons(image);
+ nsvgDelete(image);
+ return shape;
+}
+
+EmbossShape priv::select_shape() {
+ EmbossShape shape;
+ shape.depth = 10.;
+ shape.distance = 0;
+ shape.use_surface = false;
+
+ shape.svg_file_path = choose_svg_file();
+ if (shape.svg_file_path.empty())
+ return {};
+
+ NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), "mm", 96.0f);
+ ScopeGuard sg([image]() { nsvgDelete(image); });
+ shape.shapes = NSVGUtils::to_ExPolygons(image);
+
+ // TODO: get scale from file
+ shape.scale = 1.;
+
+ // Must contain some shapes !!!
+ if (shape.shapes.empty())
+ shape.shapes = default_shape();
+
+ return shape;
+}
+
+DataBasePtr priv::create_emboss_data_base(std::shared_ptr> &cancel) {
+ EmbossShape shape = priv::select_shape();
+
+ if (shape.shapes.empty())
+ // canceled selection of SVG file
+ return nullptr;
+
+ // Cancel previous Job, when it is in process
+ // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs
+ // Cancel only EmbossUpdateJob no others
+ if (cancel != nullptr)
+ cancel->store(true);
+ // create new shared ptr to cancel new job
+ cancel = std::make_shared>(false);
+
+ std::string name = priv::volume_name(shape);
+
+ return std::make_unique(name, cancel /*copy*/, std::move(shape));
+}
+
// any existing icon filename to not influence GUI
const std::string GLGizmoSVG::M_ICON_FILENAME = "cut.svg";
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
index 7ca9637393..9d810cdde9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
@@ -37,29 +37,29 @@ public:
///
/// Object part / Negative volume / Modifier
/// Define position of new volume
- void create_volume(const std::string& svg_file_path, ModelVolumeType volume_type, const Vec2d &mouse_pos);
- void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
+ /// True on succesfull start creation otherwise False
+ bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
///
/// Create new text without given position
///
/// Object part / Negative volume / Modifier
- void create_volume(const std::string &svg_file_path, ModelVolumeType volume_type);
- void create_volume(ModelVolumeType volume_type); // first open file dialog
+ /// True on succesfull start creation otherwise False
+ bool create_volume(ModelVolumeType volume_type); // first open file dialog
///
/// Check whether volume is object containing only emboss volume
///
/// Pointer to volume
/// True when object otherwise False
- static bool is_svg_object(const ModelVolume *volume);
+ static bool is_svg_object(const ModelVolume &volume);
///
/// Check whether volume has emboss data
///
/// Pointer to volume
/// True when constain emboss data otherwise False
- static bool is_svg(const ModelVolume *volume);
+ static bool is_svg(const ModelVolume &volume);
protected:
bool on_init() override;
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp
index c56b347b9b..136399bc64 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.cpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp
@@ -5,6 +5,7 @@
#include
#include // load_obj for default mesh
#include // use surface cuts
+#include // create object
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
@@ -27,6 +28,120 @@ using namespace Slic3r::Emboss;
using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
+// Private implementation for create volume and objects jobs
+namespace Slic3r::GUI::Emboss {
+///
+/// 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 !!!
+///
+struct DataCreateVolume
+{
+ // Hold data about shape
+ DataBasePtr base;
+
+ // define embossed volume type
+ ModelVolumeType volume_type;
+
+ // parent ModelObject index where to create volume
+ ObjectID object_id;
+
+ // new created volume transformation
+ std::optional trmat;
+
+ // Define which gizmo open on the success
+ GLGizmosManager::EType gizmo;
+};
+
+///
+/// Create new TextVolume on the surface of ModelObject
+/// Should not be stopped
+/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
+///
+class CreateVolumeJob : public Job
+{
+ DataCreateVolume m_input;
+ TriangleMesh m_result;
+
+public:
+ CreateVolumeJob(DataCreateVolume &&input);
+ void process(Ctl &ctl) override;
+ void finalize(bool canceled, std::exception_ptr &eptr) override;
+};
+
+///
+/// Hold neccessary data to create ModelObject in job
+/// Object is placed on bed under screen coor
+/// OR to center of scene when it is out of bed shape
+///
+struct DataCreateObject
+{
+ // Hold data about shape
+ DataBasePtr base;
+
+ // define position on screen where to create object
+ Vec2d screen_coor;
+
+ // projection property
+ Camera camera;
+
+ // shape of bed in case of create volume on bed
+ std::vector bed_shape;
+
+ // Define which gizmo open on the success
+ GLGizmosManager::EType gizmo;
+};
+
+///
+/// Create new TextObject on the platter
+/// Should not be stopped
+///
+class CreateObjectJob : public Job
+{
+ DataCreateObject m_input;
+ TriangleMesh m_result;
+ Transform3d m_transformation;
+
+public:
+ CreateObjectJob(DataCreateObject &&input);
+ void process(Ctl &ctl) override;
+ void finalize(bool canceled, std::exception_ptr &eptr) override;
+};
+
+///
+/// Hold neccessary data to create(cut) volume from surface object in job
+///
+struct CreateSurfaceVolumeData : public SurfaceVolumeData
+{
+ // Hold data about shape
+ DataBasePtr base;
+
+ // define embossed volume type
+ ModelVolumeType volume_type;
+
+ // parent ModelObject index where to create volume
+ ObjectID object_id;
+
+ // Define which gizmo open on the success
+ GLGizmosManager::EType gizmo;
+};
+
+///
+/// Cut surface from object and create cutted volume
+/// Should not be stopped
+///
+class CreateSurfaceVolumeJob : public Job
+{
+ CreateSurfaceVolumeData m_input;
+ TriangleMesh m_result;
+
+public:
+ CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input);
+ void process(Ctl &ctl) override;
+ void finalize(bool canceled, std::exception_ptr &eptr) override;
+};
+} // namespace Slic3r::GUI::Emboss
+
// private namespace
namespace priv{
// create sure that emboss object is bigger than source object [in mm]
@@ -37,12 +152,13 @@ constexpr float safe_extension = 1.0f;
///
///
///
-bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
-bool check(const DataCreateVolume &input, bool is_main_thread = false);
-bool check(const DataCreateObject &input);
-bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
-bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
-bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
+static bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
+static bool check(GLGizmosManager::EType gizmo);
+static bool check(const DataCreateVolume &input, bool is_main_thread = false);
+static bool check(const DataCreateObject &input);
+static bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
+static bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
+static bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
//
/// Try to create mesh from text
@@ -85,8 +201,9 @@ static void update_name_in_list(const ObjectList &object_list, const ModelVolume
/// Type of new volume
/// Transformation of volume inside of object
/// Text configuration and New VolumeName
-static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id,
- const ModelVolumeType type, const Transform3d trmat, const DataBase &data);
+/// Gizmo to open
+static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id, const ModelVolumeType type,
+ const std::optional& trmat, const DataBase &data, GLGizmosManager::EType gizmo);
///
/// Select Volume from objects
@@ -124,6 +241,14 @@ static OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Tr
/// Extruded object from cuted surace
static TriangleMesh cut_surface(/*const*/ DataBase &input1, const SurfaceVolumeData &input2, std::function was_canceled);
+///
+/// Copied triangles from object to be able create mesh for cut surface from
+///
+/// Source object volumes for cut surface from
+/// Source volume id
+/// Source data for cut surface from
+static SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional text_volume_id = {});
+
static void create_message(const std::string &message); // only in finalize
static bool process(std::exception_ptr &eptr);
static bool finalize(bool canceled, std::exception_ptr &eptr, const DataBase &input);
@@ -141,6 +266,11 @@ auto was_canceled(Job::Ctl &ctl, DataBase &base){
}// namespace priv
+void Slic3r::GUI::Emboss::DataBase::write(ModelVolume &volume) const{
+ volume.name = volume_name;
+ volume.emboss_shape = shape;
+}
+
/////////////////
/// Create Volume
CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input): m_input(std::move(input)){ assert(priv::check(m_input, true)); }
@@ -159,7 +289,7 @@ void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
return;
if (m_result.its.empty())
return priv::create_message(_u8L("Can't create empty volume."));
- priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, *m_input.base);
+ priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, *m_input.base, m_input.gizmo);
}
@@ -194,7 +324,7 @@ void CreateObjectJob::process(Ctl &ctl)
bed_shape_.reserve(m_input.bed_shape.size());
for (const Vec2d &p : m_input.bed_shape)
bed_shape_.emplace_back(p.cast());
- Polygon bed(bed_shape_);
+ Slic3r::Polygon bed(bed_shape_);
if (!bed.contains(bed_coor.cast()))
// mouse pose is out of build plate so create object in center of plate
bed_coor = bed.centroid().cast();
@@ -254,8 +384,8 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
// Manager::reset_all() So Gizmo could be closed before end of creation object
GLCanvas3D *canvas = plater->canvas3D();
GLGizmosManager &manager = canvas->get_gizmos_manager();
- if (manager.get_current_type() != GLGizmosManager::Emboss)
- manager.open_gizmo(GLGizmosManager::Emboss);
+ if (manager.get_current_type() != m_input.gizmo)
+ manager.open_gizmo(m_input.gizmo);
// redraw scene
canvas->reload_scene(true);
@@ -306,7 +436,7 @@ void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
if (!priv::finalize(canceled, eptr, *m_input.base))
return;
priv::create_volume(std::move(m_result), m_input.object_id,
- m_input.volume_type, m_input.transform, *m_input.base);
+ m_input.volume_type, m_input.transform, *m_input.base, m_input.gizmo);
}
/////////////////
@@ -335,143 +465,203 @@ void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr)
priv::update_volume(std::move(m_result), m_input, tr);
}
-namespace Slic3r::GUI::Emboss {
+namespace priv {
+///
+/// Check if volume type is possible use for new text volume
+///
+/// Type
+/// True when allowed otherwise false
+static bool is_valid(ModelVolumeType volume_type);
+
+///
+/// Start job for add new volume to object with given transformation
+///
+/// Define where to queue the job. e.g. wxGetApp().plater()->get_ui_job_worker()
+/// Define where to add
+/// Wanted volume transformation, when not set will be calculated after creation to be near the object
+/// Define what to emboss - shape
+/// Type of volume: Part, negative, modifier
+/// Define which gizmo open on the success
+/// Nullptr when job is sucessfully add to worker otherwise return data to be processed different way
+static bool start_create_volume_job(Worker &worker, const ModelObject &object, const std::optional& volume_tr, DataBasePtr data, ModelVolumeType volume_type, GLGizmosManager::EType gizmo);
+
+///
+/// Find volume in selected objects with closest convex hull to screen center.
+///
+/// Define where to search for closest
+/// Canvas center(dependent on camera settings)
+/// Actual objects
+/// OUT: coordinate of controid of closest volume
+/// closest volume when exists otherwise nullptr
+static const GLVolume *find_closest(
+ const Selection &selection, const Vec2d &screen_center,
+ const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center);
+
+///
+/// Start job for add object with text into scene
+///
+/// Contain worker and build shape
+/// Define params of text
+/// Screen coordinat, where to create new object laying on bed
+/// Define which gizmo open on the success
+/// True when can add job to worker otherwise FALSE
+static bool start_create_object_job(Plater &plater, DataBasePtr emboss_data, const Vec2d &coor, GLGizmosManager::EType gizmo);
+
+///
+/// Start job to create volume on the surface of object
+///
+/// scene RayCasters + Objects + Camera + worker
+/// Describe what to emboss
+/// Type of new volume
+/// Where to add
+/// Surface point is belonge to
+/// For project on surface
+/// Define which gizmo open on the success
+/// Distance from surface
+/// Angle around emboss direction
+/// True on add job to worker otherwise false
+/// Nullptr when job is sucessfully add to worker otherwise return data to be processed different way
+static DataBasePtr start_create_volume_on_surface_job(Plater &plater,
+ DataBasePtr data,
+ ModelVolumeType volume_type,
+ const Vec2d &screen_coor,
+ const GLVolume &gl_volume,
+ RaycastManager &raycaster,
+ GLGizmosManager::EType gizmo,
+ const std::optional &distance,
+ const std::optional &angle,
+ bool &success);
-SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional text_volume_id)
-{
- SurfaceVolumeData::ModelSources result;
- result.reserve(volumes.size() - 1);
- for (const ModelVolume *v : volumes) {
- if (text_volume_id.has_value() && v->id().id == *text_volume_id)
- continue;
- // skip modifiers and negative volumes, ...
- if (!v->is_model_part())
- continue;
- const TriangleMesh &tm = v->mesh();
- if (tm.empty())
- continue;
- if (tm.its.empty())
- continue;
- result.push_back({v->get_mesh_shared_ptr(), v->get_matrix()});
- }
- return result;
}
-SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume)
+namespace Slic3r::GUI::Emboss {
+
+SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &text_volume)
{
- if (text_volume == nullptr)
- return {};
- if (!text_volume->text_configuration.has_value())
- return {};
- const ModelVolumePtrs &volumes = text_volume->get_object()->volumes;
+ const ModelVolumePtrs &volumes = text_volume.get_object()->volumes;
// no other volume in object
if (volumes.size() <= 1)
return {};
- return create_sources(volumes, text_volume->id().id);
+ return priv::create_sources(volumes, text_volume.id().id);
}
-bool start_create_volume_job(
- Worker &worker, const ModelObject &object, const Transform3d volume_tr, DataBasePtr data, ModelVolumeType volume_type)
+bool start_create_volume(Plater *plater_ptr,
+ DataBasePtr data,
+ ModelVolumeType volume_type,
+ RaycastManager &raycaster,
+ unsigned char gizmo,
+ const Vec2d &mouse_pos,
+ const std::optional &distance,
+ const std::optional &angle)
{
- bool &use_surface = data->shape.use_surface;
- std::unique_ptr job;
- if (use_surface) {
- // Model to cut surface from.
- SurfaceVolumeData::ModelSources sources = create_sources(object.volumes);
- if (sources.empty()) {
- use_surface = false;
- } else {
- bool is_outside = volume_type == ModelVolumeType::MODEL_PART;
- // check that there is not unexpected volume type
- assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER);
- SurfaceVolumeData sfvd{volume_tr, is_outside, std::move(sources)};
- CreateSurfaceVolumeData surface_data{std::move(sfvd), std::move(data), volume_type, object.id()};
- job = std::make_unique(std::move(surface_data));
- }
- }
- if (!use_surface) {
- // create volume
- DataCreateVolume create_volume_data{std::move(data), volume_type, object.id(), volume_tr};
- job = std::make_unique(std::move(create_volume_data));
- }
- return queue_job(worker, std::move(job));
+ if (!priv::is_valid(volume_type))
+ return false;
+
+ assert(plater_ptr);
+ if (plater_ptr == nullptr)
+ return false;
+ Plater &plater = *plater_ptr;
+
+ GLCanvas3D *canvas_ptr = plater.get_current_canvas3D();
+ assert(canvas_ptr);
+ if (canvas_ptr == nullptr)
+ return false;
+
+ GLGizmosManager::EType gizmo_type = static_cast(gizmo);
+
+ GLVolume *gl_volume = get_first_hovered_gl_volume(*canvas_ptr);
+ if (gl_volume == nullptr)
+ // object is not under mouse position soo create object on plater
+ return priv::start_create_object_job(plater, std::move(data), mouse_pos, gizmo_type);
+
+ bool success = true;
+ DataBasePtr data2 = priv::start_create_volume_on_surface_job(plater, std::move(data),
+ volume_type, mouse_pos, *gl_volume, raycaster, gizmo_type, distance, angle, success);
+
+ // Is successfull created job for add volume on surface?
+ if (success)
+ return true;
+
+ // not success and consume data
+ if (data2 == nullptr)
+ return false;
+
+ // Can't create on coordinate try to create somewhere
+ return start_create_volume_without_position(plater_ptr, std::move(data2), volume_type, raycaster, gizmo, distance, angle);
}
-std::optional create_volume_transformation_on_surface(const Vec2d &screen_coor,
- const Camera &camera,
- const ModelVolume &volume,
- const ModelInstance &instance,
- RaycastManager &raycaster,
- GLCanvas3D &canvas,
- const std::optional &distance,
- const std::optional &angle)
+bool start_create_volume_without_position(Plater *plater_ptr,
+ DataBasePtr data,
+ ModelVolumeType volume_type,
+ RaycastManager &raycaster,
+ unsigned char gizmo,
+ const std::optional &distance,
+ const std::optional &angle)
{
- auto cond = RaycastManager::AllowVolumes({volume.id().id});
- RaycastManager::Meshes meshes = create_meshes(canvas, cond);
- raycaster.actualize(instance, &cond, &meshes);
+ if (!priv::is_valid(volume_type))
+ return false;
- std::optional hit = ray_from_camera(raycaster, screen_coor, camera, &cond);
+ assert(plater_ptr);
+ if (plater_ptr == nullptr)
+ return false;
+ Plater &plater = *plater_ptr;
- // context menu for add text could be open only by right click on an
- // object. After right click, object is selected and object_idx is set
- // also hit must exist. But there is options to add text by object list
- if (!hit.has_value())
- return {};
+ GLCanvas3D *canvas_ptr = plater.get_current_canvas3D();
+ assert(canvas_ptr);
+ if (canvas_ptr == nullptr)
+ return false;
+ const GLCanvas3D &canvas = *canvas_ptr;
- // Create result volume transformation
- Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, Slic3r::GUI::up_limit);
+ // select position by camera position and view direction
+ const Selection &selection = canvas.get_selection();
+ int object_idx = selection.get_object_idx();
- apply_transformation(angle, distance, surface_trmat);
+ Size s = canvas.get_canvas_size();
+ Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
+ const ModelObjectPtrs &objects = selection.get_model()->objects;
- return instance.get_matrix().inverse() * surface_trmat;
-}
+ GLGizmosManager::EType gizmo_type = static_cast(gizmo);
-Transform3d create_volume_transformation(const GLVolume &gl_volume, const ModelObjectPtrs &objects, float volume_height, float volume_depth)
-{
- // Transformation is inspired add generic volumes in ObjectList::load_generic_subobject
- const ModelObject *obj = objects[gl_volume.object_idx()];
- BoundingBoxf3 instance_bb = obj->instance_bounding_box(gl_volume.instance_idx());
- // Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
- Transform3d tr = gl_volume.get_instance_transformation().get_matrix_no_offset().inverse();
- Vec3d offset_tr(0, // center of instance - Can't suggest width of text before it will be created
- - instance_bb.size().y() / 2 - volume_height / 2, // under
- volume_depth / 2 - instance_bb.size().z() / 2 // lay on bed
- );
- return tr * Eigen::Translation3d(offset_tr);
-}
+ // No selected object so create new object
+ if (selection.is_empty() || object_idx < 0 ||
+ static_cast(object_idx) >= objects.size())
+ // create Object on center of screen
+ // when ray throw center of screen not hit bed it create object on center of bed
+ return priv::start_create_object_job(plater, std::move(data), screen_center, gizmo_type);
-const GLVolume *find_closest(
- const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center)
-{
- assert(closest_center != nullptr);
- const GLVolume * closest = nullptr;
- const Selection::IndicesList &indices = selection.get_volume_idxs();
- assert(!indices.empty()); // no selected volume
- if (indices.empty())
- return closest;
+ // create volume inside of selected object
+ Vec2d coor;
+ const Camera &camera = wxGetApp().plater()->get_camera();
+ const GLVolume *gl_volume = priv::find_closest(selection, screen_center, camera, objects, &coor);
- double center_sq_distance = std::numeric_limits::max();
- for (unsigned int id : indices) {
- const GLVolume *gl_volume = selection.get_volume(id);
- const ModelVolume *volume = get_model_volume(*gl_volume, objects);
- if (!volume->is_model_part())
- continue;
- Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume);
- Vec2d c = hull.centroid().cast();
- Vec2d d = c - screen_center;
- bool is_bigger_x = std::fabs(d.x()) > std::fabs(d.y());
- if ((is_bigger_x && d.x() * d.x() > center_sq_distance) || (!is_bigger_x && d.y() * d.y() > center_sq_distance))
- continue;
+ if (gl_volume == nullptr)
+ return priv::start_create_object_job(plater, std::move(data), screen_center, gizmo_type);
+
+ bool success = true;
+ DataBasePtr data2 = priv::start_create_volume_on_surface_job(plater, std::move(data),
+ volume_type, coor, *gl_volume, raycaster, gizmo_type, distance, angle, success);
- double distance = d.squaredNorm();
- if (center_sq_distance < distance)
- continue;
- center_sq_distance = distance;
+ // Is successfull created job for add volume on surface?
+ if (success)
+ return true;
- *closest_center = c;
- closest = gl_volume;
- }
- return closest;
+ // not success and consume data
+ if (data2 == nullptr)
+ return false;
+
+ // In centroid of convex hull is not hit with object. e.g. torid
+ // soo create transfomation on border of object
+
+ // there is no point on surface so no use of surface will be applied
+ if (data2->shape.use_surface)
+ data2->shape.use_surface = false;
+
+ Worker &worker = plater.get_ui_job_worker();
+ const ModelObject *object = get_model_object(*gl_volume, objects);
+ if (object == nullptr)
+ return false;
+
+ return priv::start_create_volume_job(worker, *object, {}, std::move(data2), volume_type, gizmo_type);
}
} // namespace Slic3r::GUI::Emboss
@@ -495,6 +685,13 @@ bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface)
//res &= input.text_configuration.style.prop.use_surface == use_surface;
return res;
}
+
+bool priv::check(GLGizmosManager::EType gizmo)
+{
+ assert(gizmo == GLGizmosManager::Emboss || gizmo == GLGizmosManager::Svg);
+ return gizmo == GLGizmosManager::Emboss || gizmo == GLGizmosManager::Svg;
+}
+
bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
bool check_fontfile = false;
assert(input.base != nullptr);
@@ -504,6 +701,7 @@ bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
res &= input.volume_type != ModelVolumeType::INVALID;
assert(input.object_id.id >= 0);
res &= input.object_id.id >= 0;
+ res &= check(input.gizmo);
return res;
}
bool priv::check(const DataCreateObject &input) {
@@ -517,6 +715,7 @@ bool priv::check(const DataCreateObject &input) {
res &= input.screen_coor.y() >= 0.;
assert(input.bed_shape.size() >= 3); // at least triangle
res &= input.bed_shape.size() >= 3;
+ res &= check(input.gizmo);
return res;
}
bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){
@@ -542,6 +741,7 @@ bool priv::check(const CreateSurfaceVolumeData &input, bool is_main_thread)
res &= check(*input.base, is_main_thread, use_surface);
assert(!input.sources.empty());
res &= !input.sources.empty();
+ res &= check(input.gizmo);
return res;
}
bool priv::check(const UpdateSurfaceVolumeData &input, bool is_main_thread){
@@ -711,9 +911,12 @@ void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3
UpdateJob::update_volume(volume, std::move(mesh), *data.base);
}
-void priv::create_volume(
- TriangleMesh &&mesh, const ObjectID& object_id,
- const ModelVolumeType type, const Transform3d trmat, const DataBase &data)
+void priv::create_volume(TriangleMesh &&mesh,
+ const ObjectID &object_id,
+ const ModelVolumeType type,
+ const std::optional &trmat,
+ const DataBase &data,
+ GLGizmosManager::EType gizmo)
{
GUI_App &app = wxGetApp();
Plater *plater = app.plater();
@@ -741,6 +944,13 @@ void priv::create_volume(
plater->take_snapshot(_L("Add Emboss text Volume"));
+ BoundingBoxf3 instance_bb;
+ if (!trmat.has_value()) {
+ // used for align to instance
+ size_t instance_index = 0; // must exist
+ instance_bb = obj->instance_bounding_box(instance_index);
+ }
+
// NOTE: be carefull add volume also center mesh !!!
// So first add simple shape(convex hull is also calculated)
ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type);
@@ -757,7 +967,24 @@ void priv::create_volume(
volume->source.is_from_builtin_objects = true;
volume->name = data.volume_name; // copy
- volume->set_transformation(trmat);
+
+ if (trmat.has_value()) {
+ volume->set_transformation(*trmat);
+ } else {
+ assert(!data.shape.use_surface);
+ // Create transformation for volume near from object(defined by glVolume)
+ // Transformation is inspired add generic volumes in ObjectList::load_generic_subobject
+ Vec3d volume_size = volume->mesh().bounding_box().size();
+ // Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
+ Vec3d offset_tr(0, // center of instance - Can't suggest width of text before it will be created
+ -instance_bb.size().y() / 2 - volume_size.y() / 2, // under
+ volume_size.z() / 2 - instance_bb.size().z() / 2); // lay on bed
+ // use same instance as for calculation of instance_bounding_box
+ Transform3d tr = obj->instances.front()->get_transformation().get_matrix_no_offset().inverse();
+ Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr);
+ volume->set_transformation(volume_trmat);
+ }
+
data.write(*volume);
// update printable state on canvas
@@ -935,6 +1162,26 @@ TriangleMesh priv::cut_surface(DataBase& base, const SurfaceVolumeData& input2,
return TriangleMesh(std::move(new_its));
}
+SurfaceVolumeData::ModelSources priv::create_sources(const ModelVolumePtrs &volumes, std::optional text_volume_id)
+{
+ SurfaceVolumeData::ModelSources result;
+ result.reserve(volumes.size() - 1);
+ for (const ModelVolume *v : volumes) {
+ if (text_volume_id.has_value() && v->id().id == *text_volume_id)
+ continue;
+ // skip modifiers and negative volumes, ...
+ if (!v->is_model_part())
+ continue;
+ const TriangleMesh &tm = v->mesh();
+ if (tm.empty())
+ continue;
+ if (tm.its.empty())
+ continue;
+ result.push_back({v->get_mesh_shared_ptr(), v->get_matrix()});
+ }
+ return result;
+}
+
bool priv::process(std::exception_ptr &eptr) {
if (!eptr) return false;
try {
@@ -956,6 +1203,141 @@ bool priv::finalize(bool canceled, std::exception_ptr &eptr, const DataBase &inp
return !process(eptr);
}
+bool priv::is_valid(ModelVolumeType volume_type)
+{
+ if (volume_type == ModelVolumeType::MODEL_PART ||
+ volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
+ volume_type == ModelVolumeType::PARAMETER_MODIFIER )
+ return true;
+
+ BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int) volume_type;
+ return false;
+}
+
+bool priv::start_create_volume_job(
+ Worker &worker, const ModelObject &object, const std::optional& volume_tr, DataBasePtr data, ModelVolumeType volume_type, GLGizmosManager::EType gizmo)
+{
+ bool &use_surface = data->shape.use_surface;
+ std::unique_ptr job;
+ if (use_surface) {
+ // Model to cut surface from.
+ SurfaceVolumeData::ModelSources sources = create_sources(object.volumes);
+ if (sources.empty() || !volume_tr.has_value()) {
+ use_surface = false;
+ } else {
+ bool is_outside = volume_type == ModelVolumeType::MODEL_PART;
+ // check that there is not unexpected volume type
+ assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER);
+ SurfaceVolumeData sfvd{*volume_tr, is_outside, std::move(sources)};
+ CreateSurfaceVolumeData surface_data{std::move(sfvd), std::move(data), volume_type, object.id(), gizmo};
+ job = std::make_unique(std::move(surface_data));
+ }
+ }
+ if (!use_surface) {
+ // create volume
+ DataCreateVolume create_volume_data{std::move(data), volume_type, object.id(), volume_tr, gizmo};
+ job = std::make_unique(std::move(create_volume_data));
+ }
+ return queue_job(worker, std::move(job));
+}
+
+const GLVolume * priv::find_closest(
+ const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center)
+{
+ assert(closest_center != nullptr);
+ const GLVolume *closest = nullptr;
+ const Selection::IndicesList &indices = selection.get_volume_idxs();
+ assert(!indices.empty()); // no selected volume
+ if (indices.empty())
+ return closest;
+
+ double center_sq_distance = std::numeric_limits::max();
+ for (unsigned int id : indices) {
+ const GLVolume *gl_volume = selection.get_volume(id);
+ const ModelVolume *volume = get_model_volume(*gl_volume, objects);
+ if (!volume->is_model_part())
+ continue;
+ Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume);
+ Vec2d c = hull.centroid().cast();
+ Vec2d d = c - screen_center;
+ bool is_bigger_x = std::fabs(d.x()) > std::fabs(d.y());
+ if ((is_bigger_x && d.x() * d.x() > center_sq_distance) || (!is_bigger_x && d.y() * d.y() > center_sq_distance))
+ continue;
+
+ double distance = d.squaredNorm();
+ if (center_sq_distance < distance)
+ continue;
+ center_sq_distance = distance;
+
+ *closest_center = c;
+ closest = gl_volume;
+ }
+ return closest;
+}
+
+bool priv::start_create_object_job(Plater &plater, DataBasePtr emboss_data, const Vec2d &coor, GLGizmosManager::EType gizmo)
+{
+ const Camera &camera = plater.get_camera();
+ const Pointfs &bed_shape = plater.build_volume().bed_shape();
+
+ DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape, gizmo};
+ auto job = std::make_unique(std::move(data));
+
+ Worker &worker = plater.get_ui_job_worker();
+ return queue_job(worker, std::move(job));
+}
+
+DataBasePtr priv::start_create_volume_on_surface_job(Plater &plater,
+ DataBasePtr data,
+ ModelVolumeType volume_type,
+ const Vec2d &screen_coor,
+ const GLVolume &gl_volume,
+ RaycastManager &raycaster,
+ GLGizmosManager::EType gizmo,
+ const std::optional &distance,
+ const std::optional &angle,
+ bool &success)
+{
+ success = false;
+ const ModelObjectPtrs &objects = plater.model().objects;
+ const ModelVolume* volume = get_model_volume(gl_volume, objects);
+ const ModelInstance *instance = get_model_instance(gl_volume, objects);
+ if (volume == nullptr || instance == nullptr ||
+ volume->get_object() == nullptr) {
+ // weird situation
+ assert(false);
+ return data;
+ }
+
+ const Camera &camera = plater.get_camera();
+ GLCanvas3D &canvas = *plater.get_current_canvas3D();
+
+ auto cond = RaycastManager::AllowVolumes({volume->id().id});
+ RaycastManager::Meshes meshes = create_meshes(canvas, cond);
+ raycaster.actualize(*instance, &cond, &meshes);
+ std::optional hit = ray_from_camera(raycaster, screen_coor, camera, &cond);
+
+ // context menu for add text could be open only by right click on an
+ // object. After right click, object is selected and object_idx is set
+ // also hit must exist. But there is options to add text by object list
+ if (!hit.has_value())
+ // When model is broken. It could appear that hit miss the object.
+ // So add part near by in simmilar manner as right panel do
+ return data;
+
+ // Create result volume transformation
+ Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, Slic3r::GUI::up_limit);
+
+ apply_transformation(angle, distance, surface_trmat);
+
+ Transform3d transform = instance->get_matrix().inverse() * surface_trmat;
+
+ // Try to cast ray into scene and find object for add volume
+ Worker &worker = plater.get_ui_job_worker();
+
+ success = priv::start_create_volume_job(worker, *volume->get_object(), transform, std::move(data), volume_type, gizmo);
+ return nullptr;
+}
#include
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp
index b1b43f6f83..00de9b69f9 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.hpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp
@@ -7,8 +7,7 @@
#include "libslic3r/Emboss.hpp"
#include "libslic3r/EmbossShape.hpp"
#include "libslic3r/Point.hpp" // Transform3d
-
-#include "slic3r/Utils/RaycastManager.hpp"
+#include "libslic3r/ObjectID.hpp"
#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
#include "slic3r/GUI/Camera.hpp"
@@ -17,16 +16,12 @@
// forward declarations
namespace Slic3r {
-class GLVolume;
-class ModelVolume;
-class ModelObject;
class TriangleMesh;
-typedef std::vector ModelObjectPtrs;
-typedef std::vector ModelVolumePtrs;
+class ModelVolume;
+enum class ModelVolumeType : int;
namespace GUI {
-class Selection;
class RaycastManager;
-class Worker;
+class Plater;
}}
namespace Slic3r::GUI::Emboss {
@@ -38,7 +33,7 @@ class DataBase
{
public:
DataBase(std::string volume_name, std::shared_ptr> cancel) : volume_name(volume_name), cancel(std::move(cancel)) {}
- DataBase(std::string volume_name, std::shared_ptr> cancel, EmbossShape shape)
+ DataBase(std::string volume_name, std::shared_ptr> cancel, EmbossShape&& shape)
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape))
{}
virtual ~DataBase() {}
@@ -54,11 +49,7 @@ public:
/// Write data how to reconstruct shape to volume
///
/// Data object for store emboss params
- virtual void write(ModelVolume &volume) const
- {
- volume.name = volume_name;
- volume.emboss_shape = shape;
- };
+ virtual void write(ModelVolume &volume) const;
// new volume name
std::string volume_name;
@@ -70,77 +61,7 @@ public:
// shape to emboss
EmbossShape shape;
};
-
-///
-/// 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 !!!
-///
-struct DataCreateVolume
-{
- // Hold data about shape
- std::unique_ptr base;
-
- // define embossed volume type
- ModelVolumeType volume_type;
-
- // parent ModelObject index where to create volume
- ObjectID object_id;
-
- // new created volume transformation
- Transform3d trmat;
-};
-
-///
-/// Create new TextVolume on the surface of ModelObject
-/// Should not be stopped
-/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
-///
-class CreateVolumeJob : public Job
-{
- DataCreateVolume m_input;
- TriangleMesh m_result;
-
-public:
- CreateVolumeJob(DataCreateVolume&& input);
- void process(Ctl &ctl) override;
- void finalize(bool canceled, std::exception_ptr &eptr) override;
-};
-
-///
-/// Hold neccessary data to create ModelObject in job
-/// Object is placed on bed under screen coor
-/// OR to center of scene when it is out of bed shape
-///
-struct DataCreateObject
-{
- // Hold data about shape
- std::unique_ptr base;
-
- // define position on screen where to create object
- Vec2d screen_coor;
-
- // projection property
- Camera camera;
-
- // shape of bed in case of create volume on bed
- std::vector bed_shape;
-};
-
-///
-/// Create new TextObject on the platter
-/// Should not be stopped
-///
-class CreateObjectJob : public Job
-{
- DataCreateObject m_input;
- TriangleMesh m_result;
- Transform3d m_transformation;
-public:
- CreateObjectJob(DataCreateObject&& input);
- void process(Ctl &ctl) override;
- void finalize(bool canceled, std::exception_ptr &eptr) override;
-};
+using DataBasePtr = std::unique_ptr;
///
/// Hold neccessary data to update embossed text object in job
@@ -148,7 +69,7 @@ public:
struct DataUpdate
{
// Hold data about shape
- std::unique_ptr base;
+ DataBasePtr base;
// unique identifier of volume to change
ObjectID volume_id;
@@ -212,35 +133,6 @@ struct SurfaceVolumeData
ModelSources sources;
};
-///
-/// Hold neccessary data to create(cut) volume from surface object in job
-///
-struct CreateSurfaceVolumeData : public SurfaceVolumeData{
- // Hold data about shape
- std::unique_ptr base;
-
- // define embossed volume type
- ModelVolumeType volume_type;
-
- // parent ModelObject index where to create volume
- ObjectID object_id;
-};
-
-///
-/// Cut surface from object and create cutted volume
-/// Should not be stopped
-///
-class CreateSurfaceVolumeJob : public Job
-{
- CreateSurfaceVolumeData m_input;
- TriangleMesh m_result;
-
-public:
- CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input);
- void process(Ctl &ctl) override;
- void finalize(bool canceled, std::exception_ptr &eptr) override;
-};
-
///
/// Hold neccessary data to update embossed text object in job
///
@@ -264,72 +156,43 @@ public:
///
/// Copied triangles from object to be able create mesh for cut surface from
///
-/// Source object volumes for cut surface from
-/// Source volume id
+/// Define embossed volume
/// Source data for cut surface from
-SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional text_volume_id = {});
+SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &volume);
///
-/// Copied triangles from object to be able create mesh for cut surface from
+/// Create new volume on position of mouse cursor
///
-/// Define text in object
-/// Source data for cut surface from
-SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume);
+/// canvas + camera + bed shape +
+/// Shape of emboss
+/// New created volume type
+/// Knows object in scene
+/// Define which gizmo open on the success - enum GLGizmosManager::EType
+/// Define position where to create volume
+/// Wanted additionl move in Z(emboss) direction of new created volume
+/// Wanted additionl rotation around Z of new created volume
+/// True on success otherwise False
+bool start_create_volume(Plater *plater_ptr,
+ DataBasePtr data,
+ ModelVolumeType volume_type,
+ RaycastManager &raycaster,
+ unsigned char gizmo,
+ const Vec2d &mouse_pos,
+ const std::optional &distance = {},
+ const std::optional &angle = {});
-using DataBasePtr = std::unique_ptr;
///
-/// Start job for add new volume to object with given transformation
+/// Same as previous function but without mouse position
+/// Need to suggest position or put near the selection
///
-/// Define where to queue the job. e.g. wxGetApp().plater()->get_ui_job_worker()
-/// Define where to add
-/// Wanted volume transformation
-/// Define what to emboss - shape
-/// Type of volume: Part, negative, modifier
-/// True on success otherwise false
-bool start_create_volume_job(Worker &worker, const ModelObject &object, const Transform3d volume_tr, DataBasePtr data, ModelVolumeType volume_type);
-
-///
-/// Start job for add new volume on surface of object defined by screen coor
-///
-/// Mouse position which define position
-/// Volume to find surface for create
-/// Instance to find surface for create
-/// Ability to ray cast to model
-/// Contain already used scene RayCasters
-/// Initial z move
-/// Initial z rotation
-/// Volume transformation otherwise there is no hit surface by screen coor
-std::optional create_volume_transformation_on_surface(const Vec2d &screen_coor,
- const Camera &camera,
- const ModelVolume &volume,
- const ModelInstance &instance,
- RaycastManager &raycaster,
- GLCanvas3D &canvas,
- const std::optional &distance = {},
- const std::optional &angle = {});
-
-///
-/// Create transformation for volume near from object(defined by glVolume)
-///
-/// Define object
-/// All objects
-/// Y Size of embossed volume [mm in instance]
-/// Z size of embossed volume - emboss depth[mm in instance]
-/// Transformation for new created volume
-Transform3d create_volume_transformation(const GLVolume& gl_volume, const ModelObjectPtrs &objects, float volume_height, float volume_depth);
-
-///
-/// Find volume in selected objects with closest convex hull to screen center.
-///
-/// Define where to search for closest
-/// Canvas center(dependent on camera settings)
-/// Actual objects
-/// OUT: coordinate of controid of closest volume
-/// closest volume when exists otherwise nullptr
-const GLVolume *find_closest(
- const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center);
-
+bool start_create_volume_without_position(Plater *plater_ptr,
+ DataBasePtr data,
+ ModelVolumeType volume_type,
+ RaycastManager &raycaster,
+ unsigned char gizmo,
+ const std::optional &distance = {},
+ const std::optional &angle = {});
} // namespace Slic3r::GUI
#endif // slic3r_EmbossJob_hpp_