diff --git a/src/libslic3r/EmbossShape.hpp b/src/libslic3r/EmbossShape.hpp
index 97a0f8a943..0946235d87 100644
--- a/src/libslic3r/EmbossShape.hpp
+++ b/src/libslic3r/EmbossShape.hpp
@@ -21,10 +21,10 @@ struct EmbossShape
ExPolygons shapes;
// scale of shape, multiplier to get 3d point in mm from integer shape
- double scale;
+ double scale = 1.;
// Emboss depth, Size in local Z direction
- double depth; // [in loacal mm]
+ double depth = 1.; // [in loacal mm]
// NOTE: User should see and modify mainly world size not local
// Flag that result volume use surface cutted from source objects
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
index 457471af54..56f5e433d7 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
@@ -14,6 +14,7 @@
#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
@@ -107,67 +108,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
// Private namespace with helper function for create volume
namespace priv {
-///
-/// Data for emboss job to create shape
-///
-struct TextDataBase : public DataBase
-{
- 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))
- {
- assert(this->font_file.has_value());
-
- // partialy fill shape from text configuration
- const FontProp &fp = this->text_configuration.style.prop;
- shape.depth = fp.emboss;
- shape.use_surface = fp.use_surface;
- shape.distance = fp.distance;
-
- const FontFile &ff = *this->font_file.font_file;
- shape.scale = get_text_shape_scale(fp, ff);
- }
- ///
- /// Create shape from text and font
- ///
- /// Text shape defined by configuration and font file
- EmbossShape &create_shape() override {
- if (!shape.shapes.empty())
- return shape;
-
- // create shape by configuration
- const char *text = text_configuration.text.c_str();
- const FontProp &fp = text_configuration.style.prop;
- auto was_canceled = [&c = cancel]() -> bool { return c->load(); };
- shape.shapes = text2shapes(font_file, text, fp, was_canceled);
-
- // TEST
- const FontProp &prop = text_configuration.style.prop;
- const std::optional &cn = prop.collection_number;
- unsigned int font_index = (cn.has_value()) ? *cn : 0;
- const FontFileWithCache &font = font_file;
- assert(font_index < font.font_file->infos.size());
- int unit_per_em = font.font_file->infos[font_index].unit_per_em;
- float scale = prop.size_in_mm / unit_per_em;
- float depth = prop.emboss / scale;
-
- return shape;
- }
-
- void write(ModelVolume &volume) const override
- {
- volume.text_configuration = text_configuration; // copy
-
- // discard information about rotation, should not be stored in volume
- volume.text_configuration->style.prop.angle.reset();
-
- DataBase::write(volume);
- }
-
- // Keep pointer on Data of font (glyph shapes)
- FontFileWithCache font_file;
- // font item is not used for create object
- TextConfiguration text_configuration;
-};
///
/// Check if volume type is possible use for new text volume
@@ -187,16 +127,28 @@ static std::unique_ptr create_emboss_data_base(
const std::string &text, StyleManager &style_manager, std::shared_ptr> &cancel);
///
-/// Start job for add new volume to object with given transformation
+/// Data for emboss job to create shape
///
-/// Define where to add
-/// Volume transformation
-/// Define text
-/// Type of volume
-static void start_create_volume_job(const ModelObject *object,
- const Transform3d volume_trmat,
- std::unique_ptr emboss_data,
- ModelVolumeType volume_type);
+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;
+
+// private:
+ // Keep pointer on Data of font (glyph shapes)
+ FontFileWithCache font_file;
+ // font item is not used for create object
+ 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
@@ -208,35 +160,13 @@ static void start_create_volume_job(const ModelObject *object,
/// 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(std::unique_ptr emboss_data,
- ModelVolumeType volume_type,
- const Vec2d &screen_coor,
- const GLVolume *gl_volume,
- RaycastManager &raycaster,
- GLCanvas3D &canvas);
-
-///
-/// 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(std::unique_ptr emboss_data, const Vec2d &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()
@@ -270,19 +200,15 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
m_style_manager.discard_style_changes();
set_default_text();
- GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent);
std::unique_ptr emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
- if (gl_volume != nullptr) {
- // Try to cast ray into scene and find object for add volume
- if (!priv::start_create_volume_on_surface_job(std::move(emboss_data), volume_type, mouse_pos, gl_volume, m_raycast_manager, m_parent)) {
- // When model is broken. It could appear that hit miss the object.
- // So add part near by in simmilar manner as right panel do
- create_volume(volume_type);
- }
- } else {
+ 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
- priv::start_create_object_job(std::move(emboss_data), mouse_pos);
- }
+ return priv::start_create_object_job(std::move(emboss_data), mouse_pos);
+
+ 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);
}
// Designed for create volume without information of mouse in scene
@@ -310,34 +236,26 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
// create volume inside of selected object
Vec2d coor;
- const GLVolume *vol = nullptr;
const Camera &camera = wxGetApp().plater()->get_camera();
- priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol);
- if (vol == nullptr) {
- priv::start_create_object_job(std::move(emboss_data), screen_center);
- } else if (!priv::start_create_volume_on_surface_job(std::move(emboss_data), volume_type, coor, vol, m_raycast_manager, m_parent)) {
- // in centroid of convex hull is not hit with object
+ 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);
- // TODO: do it better way
- FontProp &fp = static_cast(emboss_data.get())->text_configuration.style.prop;
// there is no point on surface so no use of surface will be applied
- if (fp.use_surface)
- fp.use_surface = false;
-
- // Transformation is inspired add generic volumes in ObjectList::load_generic_subobject
- const ModelObject *obj = objects[vol->object_idx()];
- BoundingBoxf3 instance_bb = obj->instance_bounding_box(vol->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 = vol->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 - fp.size_in_mm / 2, // under
- fp.emboss / 2 - instance_bb.size().z() / 2 // lay on bed
- );
- Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr);
- priv::start_create_volume_job(obj, volume_trmat, std::move(emboss_data), volume_type);
+ 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);
}
}
@@ -3347,6 +3265,59 @@ bool priv::is_valid(ModelVolumeType 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))
+{
+ assert(this->font_file.has_value());
+
+ // partialy fill shape from text configuration
+ const FontProp &fp = this->text_configuration.style.prop;
+ shape.depth = fp.emboss;
+ shape.use_surface = fp.use_surface;
+ shape.distance = fp.distance;
+
+ const FontFile &ff = *this->font_file.font_file;
+ shape.scale = get_text_shape_scale(fp, ff);
+}
+
+EmbossShape &priv::TextDataBase::create_shape()
+{
+ if (!shape.shapes.empty())
+ return shape;
+
+ // create shape by configuration
+ const char *text = text_configuration.text.c_str();
+ const FontProp &fp = text_configuration.style.prop;
+ auto was_canceled = [&c = cancel]() -> bool { return c->load(); };
+ shape.shapes = text2shapes(font_file, text, fp, was_canceled);
+
+ // TEST
+ const FontProp &prop = text_configuration.style.prop;
+ const std::optional &cn = prop.collection_number;
+ unsigned int font_index = (cn.has_value()) ? *cn : 0;
+ const FontFileWithCache &font = font_file;
+ assert(font_index < font.font_file->infos.size());
+ int unit_per_em = font.font_file->infos[font_index].unit_per_em;
+ float scale = prop.size_in_mm / unit_per_em;
+ float depth = prop.emboss / scale;
+
+ return shape;
+}
+
+void priv::TextDataBase::write(ModelVolume &volume) const
+{
+ volume.text_configuration = text_configuration; // copy
+
+ // discard information about rotation, should not be stored in volume
+ volume.text_configuration->style.prop.angle.reset();
+
+ // only temporary solution
+ volume.text_configuration->style.prop.use_surface = shape.use_surface;
+ volume.text_configuration->style.prop.distance = shape.distance;
+
+ DataBase::write(volume);
+}
+
std::unique_ptr priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr>& cancel)
{
// create volume_name
@@ -3380,143 +3351,48 @@ 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)
-{
- // start creation of new object
+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();
-
- // can't create new object with distance from surface
- // TODO: do it better way
- FontProp &fp = static_cast(emboss_data.get())->text_configuration.style.prop;
- if (fp.distance.has_value()) fp.distance.reset();
-
- // can't create new object with using surface
- if (fp.use_surface)
- fp.use_surface = false;
-
- // Transform3d volume_tr = priv::create_transformation_on_bed(mouse_pos, camera, bed_shape, prop.emboss / 2);
+ 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));
- Worker &worker = plater->get_ui_job_worker();
+ auto job = std::make_unique(std::move(data));
queue_job(worker, std::move(job));
}
-void priv::start_create_volume_job(const ModelObject *object,
- const Transform3d volume_trmat,
- std::unique_ptr emboss_data,
- ModelVolumeType volume_type)
+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)
{
- // TODO: do it better way
- FontProp &fp = static_cast(emboss_data.get())->text_configuration.style.prop;
- bool &use_surface = fp.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_trmat, is_outside, std::move(sources)};
- CreateSurfaceVolumeData surface_data{std::move(sfvd), std::move(emboss_data), 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));
-}
-
-bool priv::start_create_volume_on_surface_job(std::unique_ptr emboss_data,
- ModelVolumeType volume_type,
- const Vec2d &screen_coor,
- const GLVolume *gl_volume,
- RaycastManager &raycaster,
- GLCanvas3D &canvas)
-{
- assert(gl_volume != nullptr);
- 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 || static_cast(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});
-
- RaycastManager::Meshes meshes = create_meshes(canvas, cond);
- raycaster.actualize(*obj, &cond, &meshes);
-
- const Camera &camera = plater->get_camera();
- 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())
+ 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;
-
- // priv::reset_skew(hit_to_world);
- Transform3d instance = gl_volume->get_instance_transformation().get_matrix();
-
- // Create result volume transformation
- Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, Slic3r::GUI::up_limit);
-
- // TODO: find better way !!!
- const FontProp &fp = static_cast(emboss_data.get())->text_configuration.style.prop;
-
- apply_transformation(fp, surface_trmat);
- // new transformation in world coor is surface_trmat
- Transform3d volume_trmat = instance.inverse() * surface_trmat;
- start_create_volume_job(obj, volume_trmat, std::move(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);
- 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_volume = gl_volume;
}
+
+ 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)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
index af92e58f54..bf78fc3eb6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
@@ -16,7 +16,6 @@
#include "libslic3r/Emboss.hpp"
#include "libslic3r/Point.hpp"
-#include "libslic3r/Model.hpp"
#include "libslic3r/TextConfiguration.hpp"
#include
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp
index 88f2624919..c56b347b9b 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.cpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp
@@ -14,9 +14,13 @@
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
+#include "slic3r/GUI/Selection.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
#include "slic3r/GUI/format.hpp"
+#include "slic3r/GUI/3DScene.hpp"
+#include "slic3r/GUI/Jobs/Worker.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
+#include "slic3r/Utils/RaycastManager.hpp"
using namespace Slic3r;
using namespace Slic3r::Emboss;
@@ -168,6 +172,13 @@ void CreateObjectJob::process(Ctl &ctl)
if (!priv::check(m_input))
throw std::runtime_error("Bad input data for EmbossCreateObjectJob.");
+ if (m_input.base->shape.distance.has_value())
+ m_input.base->shape.distance.reset();
+
+ // can't create new object with using surface
+ if (m_input.base->shape.use_surface)
+ m_input.base->shape.use_surface = false;
+
auto was_canceled = priv::was_canceled(ctl, *m_input.base);
m_result = priv::create_mesh(*m_input.base, was_canceled, ctl);
if (was_canceled()) return;
@@ -277,36 +288,6 @@ void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
priv::update_volume(std::move(m_result), m_input);
}
-namespace Slic3r::GUI::Emboss {
-
-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)
-{
- if (text_volume == nullptr) return {};
- if (!text_volume->text_configuration.has_value()) return {};
- 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);
-}
-
-} // namespace Slic3r::GUI::Emboss
-
/////////////////
/// Create Surface volume
CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input)
@@ -354,6 +335,147 @@ void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr)
priv::update_volume(std::move(m_result), m_input, tr);
}
+namespace Slic3r::GUI::Emboss {
+
+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)
+{
+ if (text_volume == nullptr)
+ return {};
+ if (!text_volume->text_configuration.has_value())
+ return {};
+ 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);
+}
+
+bool start_create_volume_job(
+ Worker &worker, const ModelObject &object, const Transform3d volume_tr, DataBasePtr data, ModelVolumeType volume_type)
+{
+ 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));
+}
+
+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)
+{
+ 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())
+ return {};
+
+ // 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);
+
+ return instance.get_matrix().inverse() * surface_trmat;
+}
+
+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);
+}
+
+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;
+
+ 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;
+}
+
+} // namespace Slic3r::GUI::Emboss
+
////////////////////////////
/// private namespace implementation
bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface)
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp
index d909291368..b1b43f6f83 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.hpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp
@@ -6,14 +6,28 @@
#include
#include "libslic3r/Emboss.hpp"
#include "libslic3r/EmbossShape.hpp"
+#include "libslic3r/Point.hpp" // Transform3d
+
#include "slic3r/Utils/RaycastManager.hpp"
+
+#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
#include "slic3r/GUI/Camera.hpp"
+
#include "Job.hpp"
+// forward declarations
namespace Slic3r {
+class GLVolume;
class ModelVolume;
+class ModelObject;
class TriangleMesh;
-}
+typedef std::vector ModelObjectPtrs;
+typedef std::vector ModelVolumePtrs;
+namespace GUI {
+class Selection;
+class RaycastManager;
+class Worker;
+}}
namespace Slic3r::GUI::Emboss {
@@ -232,6 +246,21 @@ public:
///
struct UpdateSurfaceVolumeData : public DataUpdate, public SurfaceVolumeData{};
+///
+/// Update text volume to use surface from object
+///
+class UpdateSurfaceVolumeJob : public Job
+{
+ UpdateSurfaceVolumeData m_input;
+ TriangleMesh m_result;
+
+public:
+ // move params to private variable
+ UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input);
+ void process(Ctl &ctl) override;
+ void finalize(bool canceled, std::exception_ptr &eptr) override;
+};
+
///
/// Copied triangles from object to be able create mesh for cut surface from
///
@@ -247,21 +276,59 @@ SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, s
/// Source data for cut surface from
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume);
+using DataBasePtr = std::unique_ptr;
///
-/// Update text volume to use surface from object
+/// Start job for add new volume to object with given transformation
///
-class UpdateSurfaceVolumeJob : public Job
-{
- UpdateSurfaceVolumeData m_input;
- TriangleMesh m_result;
+/// 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);
-public:
- // move params to private variable
- UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input);
- void process(Ctl &ctl) override;
- void finalize(bool canceled, std::exception_ptr &eptr) override;
-};
+///
+/// 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);
} // namespace Slic3r::GUI