diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index 8b07741ed3..0ae1b8870a 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -508,15 +508,17 @@ void MenuFactory::append_menu_item_add_text(wxMenu* menu, ModelVolumeType type,
assert(emboss != nullptr);
if (emboss == nullptr) return;
- auto screen_position = canvas->get_popup_menu_position();
- assert(screen_position.has_value());
- if (!screen_position.has_value()) return;
-
ModelVolumeType volume_type = type;
// no selected object means create new object
if (volume_type == ModelVolumeType::INVALID)
volume_type = ModelVolumeType::MODEL_PART;
- emboss->create_volume(volume_type, *screen_position);
+
+ auto screen_position = canvas->get_popup_menu_position();
+ if (screen_position.has_value()) {
+ emboss->create_volume(volume_type, *screen_position);
+ } else {
+ emboss->create_volume(volume_type);
+ }
};
if ( type == ModelVolumeType::MODEL_PART
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
index 6d763e7786..7a853e7187 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
@@ -52,7 +52,7 @@
#define SHOW_OFFSET_DURING_DRAGGING // when drag with text over surface visualize used center
#define SHOW_IMGUI_ATLAS
#define SHOW_ICONS_TEXTURE
-#define SHOW_FINE_POSITION
+#define SHOW_FINE_POSITION // draw convex hull around volume
#define SHOW_WX_WEIGHT_INPUT
#define DRAW_PLACE_TO_ADD_TEXT
#endif // ALLOW_DEBUG_MODE
@@ -140,34 +140,7 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
// paste HEX unicode into notepad move cursor after unicode press [alt] + [x]
}
-void GLGizmoEmboss::set_fine_position()
-{
- const Selection &selection = m_parent.get_selection();
- const Selection::IndicesList indices = selection.get_volume_idxs();
- // no selected volume
- if (indices.empty()) return;
- const GLVolume *volume = selection.get_volume(*indices.begin());
- // bad volume selected (e.g. deleted one)
- if (volume == nullptr) return;
-
- const Camera &camera = wxGetApp().plater()->get_camera();
- Polygon hull = CameraUtils::create_hull2d(camera, *volume);
-
- const ImVec2 &windows_size = get_minimal_window_size();
- Size c_size = m_parent.get_canvas_size();
- ImVec2 canvas_size(c_size.get_width(), c_size.get_height());
- ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, canvas_size);
- m_set_window_offset = offset;
- return;
-
- Polygon rect({Point(offset.x, offset.y),
- Point(offset.x + windows_size.x, offset.y),
- Point(offset.x + windows_size.x, offset.y + windows_size.y),
- Point(offset.x, offset.y + windows_size.y)});
- ImGuiWrapper::draw(hull);
- ImGuiWrapper::draw(rect);
-}
-
+// Private namespace with helper function for create volume
namespace priv {
///
@@ -178,52 +151,143 @@ namespace priv {
/// Base data for emboss text
static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager);
+static bool is_valid(ModelVolumeType volume_type);
+
+///
+/// Start job for add new volume to object with given transformation
+///
+/// Define where to add
+/// Volume transformation
+/// Define text
+/// Type of volume
+static void start_create_volume_job(const ModelObject *object,
+ const Transform3d volume_trmat,
+ DataBase &emboss_data,
+ ModelVolumeType volume_type);
+
+static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas);
+
+///
+/// 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
+/// True when start creation, False when there is no hit surface by screen coor
+static bool start_create_volume_on_surface_job(DataBase &emboss_data,
+ ModelVolumeType volume_type,
+ const Vec2d &screen_coor,
+ const GLVolume *gl_volume,
+ RaycastManager &raycaster);
+
+///
+/// Find volume in selected object with closest convex hull to screen center.
+/// Return
+///
+/// Define where to search for closest
+/// Canvas center(dependent on camera settings)
+/// Actual objects
+/// OUT: coordinate of controid of closest volume
+/// OUT: closest volume
+static void find_closest_volume(const Selection &selection,
+ const Vec2d &screen_center,
+ const Camera &camera,
+ const ModelObjectPtrs &objects,
+ Vec2d *closest_center,
+ const GLVolume **closest_volume);
+
+///
+/// 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(DataBase &emboss_data, const Vec2d &coor);
+
static void message_disable_cut_surface(){
wxMessageBox(_L("Can NOT cut surface from nothing. Function 'use surface' was disabled for this text."),
_L("Disable 'use surface' from style"), wxOK | wxICON_WARNING);}
+///
+/// Create transformation for new created emboss object by mouse position
+///
+/// Define where to add object
+/// Actual camera view
+/// Define shape of bed for its center and check that coor is on bed center
+/// Emboss size / 2
+/// Transformation for create text on bed
+static Transform3d create_transformation_on_bed(const Vec2d &screen_coor,
+ const Camera &camera,
+ const std::vector &bed_shape,
+ double z);
} // namespace priv
+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;
+}
+
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
{
- assert(volume_type == ModelVolumeType::MODEL_PART ||
- volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
- volume_type == ModelVolumeType::PARAMETER_MODIFIER);
+ if (!priv::is_valid(volume_type)) return;
+ if (!m_gui_cfg.has_value()) initialize();
+ set_default_text();
+ m_style_manager.discard_style_changes();
+
+ GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent);
+ DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
+ // Try to cast ray into scene and find object for add volume
+ if (priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager))
+ // object found
+ return;
+
+ // object is not under mouse position soo create object on plater
+ priv::start_create_object_job(emboss_data, mouse_pos);
+}
+
+void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
+{
+ if (!priv::is_valid(volume_type)) return;
if (!m_gui_cfg.has_value()) initialize();
set_default_text();
m_style_manager.discard_style_changes();
- Vec2d screen_coor = mouse_pos;
- if (mouse_pos.x() < 0 || mouse_pos.y() < 0) {
- // use center of screen
- auto screen_size = m_parent.get_canvas_size();
- screen_coor.x() = screen_size.get_width() / 2.;
- screen_coor.y() = screen_size.get_height() / 2.;
+ // 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.);
+ DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
+ if (!selection.is_empty() && object_idx >= 0) {
+ // create volume inside of object
+ const Plater &plater = *wxGetApp().plater();
+ const Camera &camera = plater.get_camera();
+ const ModelObjectPtrs &objects = wxGetApp().model().objects;
+
+ Vec2d coor;
+ const GLVolume *vol = nullptr;
+ priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol);
+ if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) {
+ assert(vol != nullptr);
+ // in centroid of convex hull is not hit with object
+ // soo create transfomation on border of object
+ const ModelObject *obj = objects[vol->object_idx()];
+ const BoundingBoxf3& bb = obj->bounding_box();
+ Transform3d volume_trmat(Eigen::Translation3d(bb.max.x(), 0., 0.));
+ priv::start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
+ }
+ } else {
+ // 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(emboss_data, screen_center);
}
-
- if (start_volume_creation(volume_type, screen_coor)) return;
-
- // start creation of new object
- Plater* plater = wxGetApp().plater();
- const Camera &camera = plater->get_camera();
- const Pointfs &bed_shape = plater->build_volume().bed_shape();
- // TODO: Fix double creation of base data (first is inside function start_volume_creation)
- DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
- FontProp& prop = emboss_data.text_configuration.style.prop;
-
- // can't create new object with using surface
- if (prop.use_surface) {
- priv::message_disable_cut_surface();
- prop.use_surface = false;
- }
-
- // can't create new object with distance from surface
- if (prop.distance.has_value()) prop.distance.reset();
-
- DataCreateObject data{std::move(emboss_data), screen_coor, camera, bed_shape};
- auto job = std::make_unique(std::move(data));
- Worker &worker = plater->get_ui_job_worker();
- queue_job(worker, std::move(job));
}
bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
@@ -555,7 +619,7 @@ static void draw_fine_position(const Selection &selection,
const Size &canvas,
const ImVec2 &windows_size)
{
- const Selection::IndicesList indices = selection.get_volume_idxs();
+ const Selection::IndicesList& indices = selection.get_volume_idxs();
// no selected volume
if (indices.empty()) return;
const GLVolume *volume = selection.get_volume(*indices.begin());
@@ -835,81 +899,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
-bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor)
-{
- Plater* plater = wxGetApp().plater();
-
- const Selection &selection = m_parent.get_selection();
- if (selection.is_empty()) return false;
-
- int hovered_id_signed = m_parent.get_first_hover_volume_idx();
- if (hovered_id_signed < 0) return false;
-
- size_t hovered_id = static_cast(hovered_id_signed);
- auto &volumes = m_parent.get_volumes().volumes;
- if (hovered_id >= volumes.size()) return false;
-
- int object_idx_signed = selection.get_object_idx();
- if (object_idx_signed < 0) return false;
-
- size_t object_idx = static_cast(object_idx_signed);
- auto &objects = plater->model().objects;
- if (object_idx >= objects.size()) return false;
-
- ModelObject *obj = objects[object_idx];
- m_raycast_manager.actualize(obj);
-
- const Camera &camera = plater->get_camera();
- std::optional hit =
- m_raycast_manager.unproject(screen_coor, camera);
-
- // 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 proper behavior when hit doesn't
- // exists. When this assert appear distquish remove of it.
- assert(hit.has_value());
- if (!hit.has_value()) return false;
-
- Transform3d hit_object_trmat = m_raycast_manager.get_transformation(hit->tr_key);
-
- GLVolume *gl_volume = volumes[hovered_id];
- Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix();
-
- DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager);
-
- // Create result volume transformation
- Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal);
- const FontProp& font_prop = emboss_data.text_configuration.style.prop;
- apply_transformation(font_prop, surface_trmat);
- Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat;
-
- bool &use_surface = emboss_data.text_configuration.style.prop.use_surface;
- std::unique_ptr job;
- if (use_surface) {
- // Model to cut surface from.
- SurfaceVolumeData::ModelSources sources = create_sources(obj->volumes);
- if (sources.empty()) {
- priv::message_disable_cut_surface();
- 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);
- CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside,
- std::move(sources), volume_type, object_idx_signed};
- job = std::make_unique(std::move(surface_data));
- }
- }
- if (!use_surface) {
- // create volume
- DataCreateVolume data{std::move(emboss_data), volume_type, object_idx_signed, volume_trmat};
- job = std::make_unique(std::move(data));
- }
- Worker &worker = plater->get_ui_job_worker();
- queue_job(worker, std::move(job));
- return true;
-}
-
#include "imgui/imgui_internal.h" // to unfocus input --> ClearActiveID
void GLGizmoEmboss::check_selection()
{
@@ -1562,6 +1551,9 @@ void GLGizmoEmboss::draw_font_preview(FaceName& face, bool is_visible)
m_face_names.texture_index = texture_index;
face.texture_index = texture_index;
+ // clear texture
+
+
// render text to texture
FontImageData data{
text,
@@ -2596,6 +2588,34 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle)
m_parent.do_rotate(snapshot_name);
}
+void GLGizmoEmboss::set_fine_position()
+{
+ const Selection &selection = m_parent.get_selection();
+ const Selection::IndicesList indices = selection.get_volume_idxs();
+ // no selected volume
+ if (indices.empty()) return;
+ const GLVolume *volume = selection.get_volume(*indices.begin());
+ // bad volume selected (e.g. deleted one)
+ if (volume == nullptr) return;
+
+ const Camera &camera = wxGetApp().plater()->get_camera();
+ Polygon hull = CameraUtils::create_hull2d(camera, *volume);
+
+ const ImVec2 &windows_size = get_minimal_window_size();
+ Size c_size = m_parent.get_canvas_size();
+ ImVec2 canvas_size(c_size.get_width(), c_size.get_height());
+ ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, canvas_size);
+ m_set_window_offset = offset;
+ return;
+
+ Polygon rect({Point(offset.x, offset.y),
+ Point(offset.x + windows_size.x, offset.y),
+ Point(offset.x + windows_size.x, offset.y + windows_size.y),
+ Point(offset.x, offset.y + windows_size.y)});
+ ImGuiWrapper::draw(hull);
+ ImGuiWrapper::draw(rect);
+}
+
void GLGizmoEmboss::draw_advanced()
{
const auto &ff = m_style_manager.get_font_file_with_cache();
@@ -2965,40 +2985,6 @@ bool GLGizmoEmboss::choose_svg_file()
//return add_volume(name, its);
}
-DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& style_manager)
-{
- auto create_volume_name = [&]() {
- bool contain_enter = text.find('\n') != std::string::npos;
- std::string text_fixed;
- if (contain_enter) {
- // change enters to space
- text_fixed = text; // copy
- std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' ');
- }
- return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : text);
- };
-
- auto create_configuration = [&]() -> TextConfiguration {
- if (!style_manager.is_activ_font()) {
- std::string default_text_for_emboss = _u8L("Embossed text");
- EmbossStyle es = style_manager.get_style();
- TextConfiguration tc{es, default_text_for_emboss};
- // TODO: investigate how to initialize
- return tc;
- }
-
- EmbossStyle &es = style_manager.get_style();
- // actualize font path - during changes in gui it could be corrupted
- // volume must store valid path
- assert(style_manager.get_wx_font().has_value());
- assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0);
- // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font());
- return TextConfiguration{es, text};
- };
-
- return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()};
-}
-
bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
{
if (volume == nullptr) return false;
@@ -3251,5 +3237,202 @@ std::string GLGizmoEmboss::get_file_name(const std::string &file_path)
return file_path.substr(offset, count);
}
+/////////////
+// priv namespace implementation
+///////////////
+
+DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& style_manager)
+{
+ auto create_volume_name = [&]() {
+ bool contain_enter = text.find('\n') != std::string::npos;
+ std::string text_fixed;
+ if (contain_enter) {
+ // change enters to space
+ text_fixed = text; // copy
+ std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' ');
+ }
+ return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : text);
+ };
+
+ auto create_configuration = [&]() -> TextConfiguration {
+ if (!style_manager.is_activ_font()) {
+ std::string default_text_for_emboss = _u8L("Embossed text");
+ EmbossStyle es = style_manager.get_style();
+ TextConfiguration tc{es, default_text_for_emboss};
+ // TODO: investigate how to initialize
+ return tc;
+ }
+
+ EmbossStyle &es = style_manager.get_style();
+ // actualize font path - during changes in gui it could be corrupted
+ // volume must store valid path
+ assert(style_manager.get_wx_font().has_value());
+ assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0);
+ // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font());
+ return TextConfiguration{es, text};
+ };
+
+ return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()};
+}
+
+
+Transform3d priv::create_transformation_on_bed(const Vec2d &screen_coor, const Camera &camera, const std::vector &bed_shape, double z)
+{
+ // Create new object
+ // calculate X,Y offset position for lay on platter in place of
+ // mouse click
+ Vec2d bed_coor = CameraUtils::get_z0_position(camera, screen_coor);
+
+ // check point is on build plate:
+ Points bed_shape_;
+ bed_shape_.reserve(bed_shape.size());
+ for (const Vec2d &p : bed_shape) bed_shape_.emplace_back(p.cast());
+ 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();
+
+ Vec3d offset(bed_coor.x(), bed_coor.y(), z);
+ // offset -= m_result.center();
+ Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
+ return Transform3d(tt);
+}
+
+void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor)
+{
+ // start creation of new object
+ Plater *plater = wxGetApp().plater();
+ const Camera &camera = plater->get_camera();
+ const Pointfs &bed_shape = plater->build_volume().bed_shape();
+
+ // can't create new object with distance from surface
+ FontProp &prop = emboss_data.text_configuration.style.prop;
+ if (prop.distance.has_value()) prop.distance.reset();
+
+ // can't create new object with using surface
+ if (prop.use_surface) {
+ priv::message_disable_cut_surface();
+ prop.use_surface = false;
+ }
+
+ // Transform3d volume_tr = priv::create_transformation_on_bed(mouse_pos, camera, bed_shape, prop.emboss / 2);
+ DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape};
+ auto job = std::make_unique(std::move(data));
+ Worker &worker = plater->get_ui_job_worker();
+ queue_job(worker, std::move(job));
+}
+
+void priv::start_create_volume_job(const ModelObject *object,
+ const Transform3d volume_trmat,
+ DataBase &emboss_data,
+ ModelVolumeType volume_type)
+{
+ bool &use_surface = emboss_data.text_configuration.style.prop.use_surface;
+ std::unique_ptr job;
+ if (use_surface) {
+ // Model to cut surface from.
+ SurfaceVolumeData::ModelSources sources = create_sources(object->volumes);
+ if (sources.empty()) {
+ priv::message_disable_cut_surface();
+ 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);
+ CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside,
+ std::move(sources), volume_type, object->id()};
+ job = std::make_unique(std::move(surface_data));
+ }
+ }
+ if (!use_surface) {
+ // create volume
+ DataCreateVolume data{std::move(emboss_data), volume_type, object->id(), volume_trmat};
+ job = std::make_unique(std::move(data));
+ }
+
+ Plater *plater = wxGetApp().plater();
+ Worker &worker = plater->get_ui_job_worker();
+ queue_job(worker, std::move(job));
+}
+
+GLVolume * priv::get_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(hovered_id_signed);
+ const GLVolumePtrs &volumes = canvas.get_volumes().volumes;
+ if (hovered_id >= volumes.size()) return nullptr;
+
+ return volumes[hovered_id];
+}
+
+bool priv::start_create_volume_on_surface_job(
+ DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster)
+{
+ if (gl_volume == nullptr) return false;
+ Plater *plater = wxGetApp().plater();
+ const ModelObjectPtrs &objects = plater->model().objects;
+
+ int object_idx = gl_volume->object_idx();
+ if (object_idx < 0 || object_idx >= objects.size()) return false;
+ ModelObject *obj = objects[object_idx];
+ size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id;
+ auto cond = RaycastManager::AllowVolumes({vol_id});
+ raycaster.actualize(obj, &cond);
+
+ const Camera &camera = plater->get_camera();
+ std::optional hit = raycaster.unproject(screen_coor, camera);
+
+ // 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 false;
+
+ Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key);
+ Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix();
+
+ // Create result volume transformation
+ Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal);
+ const FontProp &font_prop = emboss_data.text_configuration.style.prop;
+ apply_transformation(font_prop, surface_trmat);
+ Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat;
+ start_create_volume_job(obj, volume_trmat, emboss_data, volume_type);
+ return true;
+}
+
+void priv::find_closest_volume(const Selection &selection,
+ const Vec2d &screen_center,
+ const Camera &camera,
+ const ModelObjectPtrs &objects,
+ Vec2d *closest_center,
+ const GLVolume **closest_volume)
+{
+ assert(closest_center != nullptr);
+ assert(closest_volume != nullptr);
+ assert(*closest_volume == nullptr);
+ const Selection::IndicesList &indices = selection.get_volume_idxs();
+ assert(!indices.empty()); // no selected volume
+ if (indices.empty()) return;
+
+ double center_sq_distance = std::numeric_limits::max();
+ for (unsigned int id : indices) {
+ const GLVolume *gl_volume = selection.get_volume(id);
+ ModelVolume *volume = priv::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_volume = gl_volume;
+ }
+}
+
// any existing icon filename to not influence GUI
const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg";
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
index f8a6a4d1f1..092041e96e 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
@@ -44,13 +44,13 @@ public:
///
/// Object part / Negative volume / Modifier
/// Define position of new volume
- void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos = Vec2d(-1,-1));
+ void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
///
- /// Move window for edit emboss text near to embossed object
- /// NOTE: embossed object must be selected
+ /// Create new text without given position
///
- void set_fine_position();
+ /// Object part / Negative volume / Modifier
+ void create_volume(ModelVolumeType volume_type);
protected:
bool on_init() override;
@@ -91,8 +91,6 @@ private:
// localized default text
void set_default_text();
- bool start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor);
-
void check_selection();
ModelVolume *get_selected_volume();
// create volume from text - main functionality
@@ -125,6 +123,12 @@ private:
void do_translate(const Vec3d& relative_move);
void do_rotate(float relative_z_angle);
+ ///
+ /// Move window for edit emboss text near to embossed object
+ /// NOTE: embossed object must be selected
+ ///
+ void set_fine_position();
+
///
/// Reversible input float with option to restor default value
/// TODO: make more general, static and move to ImGuiWrapper
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp
index f49e8499bd..56d8c7e454 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.cpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp
@@ -69,11 +69,11 @@ static void update_volume(TriangleMesh &&mesh, const DataUpdate &data);
/// Add new volume to object
///
/// triangles of new volume
-/// Object where to add volume
+/// Object where to add volume
/// Type of new volume
/// Transformation of volume inside of object
/// Text configuration and New VolumeName
-static void create_volume(TriangleMesh &&mesh, const size_t object_idx,
+static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id,
const ModelVolumeType type, const Transform3d trmat, const DataBase &data);
///
@@ -147,7 +147,7 @@ void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
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_idx, m_input.volume_type, m_input.trmat, m_input);
+ priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, m_input);
}
@@ -320,7 +320,7 @@ void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
// TODO: Find better way to Not center volume data when add !!!
TriangleMesh mesh = m_result; // Part1: copy
- priv::create_volume(std::move(m_result), m_input.object_idx,
+ priv::create_volume(std::move(m_result), m_input.object_id,
m_input.volume_type, m_input.text_tr, m_input);
// Part2: update volume data
@@ -385,10 +385,8 @@ bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
bool res = check((DataBase) input, check_fontfile);
assert(input.volume_type != ModelVolumeType::INVALID);
res &= input.volume_type != ModelVolumeType::INVALID;
- assert(input.object_idx >= 0);
- res &= input.object_idx >= 0;
- if (is_main_thread)
- assert((size_t)input.object_idx < wxGetApp().model().objects.size());
+ assert(input.object_id.id >= 0);
+ res &= input.object_id.id >= 0;
return res;
}
bool priv::check(const DataCreateObject &input) {
@@ -571,7 +569,7 @@ void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data)
}
void priv::create_volume(
- TriangleMesh &&mesh, const size_t object_idx,
+ TriangleMesh &&mesh, const ObjectID& object_id,
const ModelVolumeType type, const Transform3d trmat, const DataBase &data)
{
GUI_App &app = wxGetApp();
@@ -580,18 +578,26 @@ void priv::create_volume(
GLCanvas3D *canvas = plater->canvas3D();
ModelObjectPtrs &objects = plater->model().objects;
+ ModelObject *obj = nullptr;
+ size_t object_idx = 0;
+ for (; object_idx < objects.size(); ++object_idx) {
+ ModelObject *o = objects[object_idx];
+ if (o->id() == object_id) {
+ obj = o;
+ break;
+ }
+ }
+
// Parent object for text volume was propably removed.
// Assumption: User know what he does, so text volume is no more needed.
- if (objects.size() <= object_idx)
- return priv::create_message(_u8L("Bad object index to create volume."));
+ if (obj == nullptr)
+ return priv::create_message(_u8L("Bad object to create volume."));
if (mesh.its.empty())
return priv::create_message(_u8L("Can't create empty volume."));
plater->take_snapshot(_L("Add Emboss text Volume"));
- ModelObject *obj = objects[object_idx];
-
// 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);
@@ -618,7 +624,7 @@ void priv::create_volume(
// select only actual volume
// when new volume is created change selection to this volume
auto add_to_selection = [volume](const ModelVolume *vol) { return vol == volume; };
- wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection);
+ wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection);
if (!sel.IsEmpty()) obj_list->select_item(sel.front());
// update printable state on canvas
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp
index cf64044bf0..c8ef7fee64 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.hpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp
@@ -41,7 +41,7 @@ struct DataCreateVolume : public DataBase
ModelVolumeType volume_type;
// parent ModelObject index where to create volume
- int object_idx;
+ ObjectID object_id;
// new created volume transformation
Transform3d trmat;
@@ -178,7 +178,7 @@ struct CreateSurfaceVolumeData : public DataBase, public SurfaceVolumeData{
ModelVolumeType volume_type;
// parent ModelObject index where to create volume
- int object_idx;
+ ObjectID object_id;
};
///