diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 6e1922a16..54dad8c68 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -116,7 +116,6 @@ static bool is_text_empty(const std::string &text){ return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; } - } // namespace GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) @@ -909,6 +908,32 @@ static inline void execute_job(std::shared_ptr j) }); } +static UseSurfaceData::ModelSources get_sources_to_cut_surface_from( + const ModelVolume *text_volume) +{ + if (text_volume == nullptr) return {}; + if (!text_volume->text_configuration.has_value()) return {}; + const auto &volumes = text_volume->get_object()->volumes; + // no other volume in object + if (volumes.size() <= 1) return {}; + + UseSurfaceData::ModelSources result; + // Improve create object from part or use gl_volume + // Get first model part in object + for (const ModelVolume *v : volumes) { + if (v->id() == text_volume->id()) continue; + if (!v->is_model_part()) continue; + const TriangleMesh &tm = v->mesh(); + if (tm.empty()) continue; + if (tm.its.empty()) continue; + UseSurfaceData::ModelSource ms = {tm.its, + v->get_transformation().get_matrix(), + tm.bounding_box()}; + result.push_back(std::move(ms)); + } + return result; +} + bool GLGizmoEmboss::process() { // no volume is selected -> selection from right panel @@ -940,8 +965,8 @@ bool GLGizmoEmboss::process() const TextConfiguration &tc = data.text_configuration; if (tc.font_item.prop.use_surface) { // Model to cut surface from. - const ModelVolume *mesh = get_volume_to_cut_surface_from(); - if (mesh == nullptr) return false; + auto sources = get_sources_to_cut_surface_from(m_volume); + if (sources.empty()) return false; Transform3d text_tr = m_volume->get_matrix(); auto& fix_3mf = m_volume->text_configuration->fix_3mf_tr; @@ -952,12 +977,8 @@ bool GLGizmoEmboss::process() // check that there is not unexpected volume type assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); - UseSurfaceData surface_data{std::move(data), - text_tr, - is_outside, - mesh->mesh().its /*copy*/, - mesh->get_matrix(), - mesh->mesh().bounding_box()}; + UseSurfaceData surface_data{std::move(data), text_tr, is_outside, + std::move(sources)}; job = std::make_unique(std::move(surface_data)); } else { job = std::make_unique(std::move(data)); @@ -1024,29 +1045,6 @@ void GLGizmoEmboss::select_stored_font_item() m_stored_wx_font = WxFontUtils::load_wxFont(m_stored_font_item->path); } -const ModelVolume * GLGizmoEmboss::get_volume_to_cut_surface_from() -{ - if (m_volume == nullptr) return nullptr; - if (!m_volume->text_configuration.has_value()) return nullptr; - const auto &volumes = m_volume->get_object()->volumes; - // no other volume in object - if (volumes.size() <= 1) return nullptr; - - // Improve create object from part or use gl_volume - // Get first model part in object - for (const ModelVolume *v : volumes) { - if (v->id() == m_volume->id()) continue; - if (!v->is_model_part()) continue; - const TriangleMesh &tm = v->mesh(); - if (tm.empty()) continue; - if (tm.its.empty()) continue; - return v; - } - - // No valid source volume in objct volumes - return nullptr; -} - void GLGizmoEmboss::draw_window() { #ifdef ALLOW_DEBUG_MODE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 649e62bd5..44dfdeb05 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -116,12 +116,6 @@ private: void do_translate(const Vec3d& relative_move); void do_rotate(float relative_z_angle); - /// - /// Choose valid source Volume to project on(cut surface from). - /// - /// ModelVolume to project on - const ModelVolume *get_volume_to_cut_surface_from(); - /// /// 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 5d1634a19..ad33c3d6a 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -22,20 +22,35 @@ using namespace GUI; // private namespace namespace priv{ -// -/// Create mesh from text +/// +/// Assert check of inputs data /// -/// Text to convert on mesh -/// Define shape of characters. -/// NOTE: Can't be const cache glyphs -/// Property of font -/// Lambda returning bool to check if process was canceled +/// +/// +bool check(const EmbossDataBase &input, bool check_fontfile = true); +bool check(const EmbossDataCreateVolume &input, bool is_main_thread = false); +bool check(const EmbossDataCreateObject &input); +bool check(const EmbossDataUpdate &input, bool is_main_thread = false); +bool check(const UseSurfaceData &input, bool is_main_thread = false); + +// +/// Try to create mesh from text +/// +/// Text to convert on mesh +/// + Shape of characters + Property of font +/// Font file with cache +/// NOTE: Cache glyphs is changed +/// To check if process was canceled /// Triangle mesh model template -static TriangleMesh create_mesh(const char *text, - Emboss::FontFileWithCache &font, - const FontProp &font_prop, - Fnc was_canceled); +static TriangleMesh try_create_mesh(const EmbossDataBase &input, + Emboss::FontFileWithCache &font, + Fnc was_canceled); +template +static TriangleMesh create_mesh(EmbossDataBase &input, + Fnc was_canceled, + Job::Ctl &ctl); + /// /// Create default mesh for embossed text /// @@ -49,6 +64,14 @@ static TriangleMesh create_default_mesh(); /// Text configuration, ... static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data); +/// +/// Select Volume from objects +/// +/// All objects in scene +/// Identifier of volume in object +/// Pointer to volume when exist otherwise nullptr +static ModelVolume *get_volume(ModelObjectPtrs &objects, + const ObjectID &volume_id); /// /// extract scale in 2d /// @@ -65,7 +88,7 @@ static double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff); /// Bounding box 2d of shape to center result /// Bounding box 3d of model volume for projection ranges /// Orthogonal cut_projection -static std::unique_ptr create_projection_for_cut( +static Emboss::OrthoProject create_projection_for_cut( Transform3d tr, double shape_scale, const BoundingBox &shape_bb, @@ -79,28 +102,33 @@ static std::unique_ptr create_projection_for_cut( /// Text voliume transformation inside object /// Cutted surface from model /// Projection -static std::unique_ptr create_emboss_projection( +static Emboss::OrthoProject3f create_emboss_projection( bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut); +static void create_message(const std::string &message); // only in finalize +static bool process(std::exception_ptr &eptr); + +class EmbossJobException: public std::exception { +public: EmbossJobException(char const *const message) + : std::exception(message) + {} +}; } ///////////////// /// Create Volume EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input) : m_input(std::move(input)) -{} +{ + assert(priv::check(m_input, true)); +} void EmbossCreateVolumeJob::process(Ctl &ctl) { - // It is neccessary to create some shape - // Emboss text window is opened by creation new emboss text object - const char *text = m_input.text_configuration.text.c_str(); - FontProp &prop = m_input.text_configuration.font_item.prop; + if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob."); auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; - m_result = priv::create_mesh(text, m_input.font_file, prop, was_canceled); - if (m_result.its.empty()) m_result = priv::create_default_mesh(); - + m_result = priv::create_mesh(m_input, was_canceled, ctl); if (was_canceled()) return; - + // Create new volume inside of object const FontProp &font_prop = m_input.text_configuration.font_item.prop; Transform3d surface_trmat = Emboss::create_transformation_onto_surface( @@ -110,25 +138,36 @@ void EmbossCreateVolumeJob::process(Ctl &ctl) { m_input.hit_object_tr * surface_trmat; } -void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &) { - if (canceled) return; +void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { + // doesn't care about exception when process was canceled by user + if (canceled) { + eptr = nullptr; + return; + } + if (priv::process(eptr)) return; + if (m_result.its.empty()) + return priv::create_message(_u8L("Can't create empty volume.")); - GUI_App &app = wxGetApp(); - Plater *plater = app.plater(); - ObjectList *obj_list = app.obj_list(); - GLCanvas3D *canvas = plater->canvas3D(); - Model &model = plater->model(); + GUI_App &app = wxGetApp(); + Plater *plater = app.plater(); + ObjectList *obj_list = app.obj_list(); + GLCanvas3D *canvas = plater->canvas3D(); + ModelObjectPtrs &objects = plater->model().objects; // create volume in object size_t object_idx = m_input.object_idx; - assert(model.objects.size() > object_idx); - if (model.objects.size() <= object_idx) return; + + // 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; Plater::TakeSnapshot snapshot(plater, _L("Add Emboss text Volume")); - ModelObject *obj = model.objects[object_idx]; + ModelObject *obj = objects[object_idx]; ModelVolumeType type = m_input.volume_type; - ModelVolume *volume = obj->add_volume(std::move(m_result), type); + + ModelVolume *volume = obj->add_volume(std::move(m_result), type); // set a default extruder value, since user can't add it manually volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -149,7 +188,7 @@ void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &) { return vol == volume; }; wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection( - (int) object_idx, add_to_selection); + m_input.object_idx, add_to_selection); if (!sel.IsEmpty()) obj_list->select_item(sel.front()); // update printable state on canvas @@ -172,22 +211,17 @@ void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &) { /// Create Object EmbossCreateObjectJob::EmbossCreateObjectJob(EmbossDataCreateObject &&input) : m_input(std::move(input)) -{} +{ + assert(priv::check(m_input)); +} void EmbossCreateObjectJob::process(Ctl &ctl) { - // It is neccessary to create some shape - // Emboss text window is opened by creation new emboss text object - const char *text = m_input.text_configuration.text.c_str(); - FontProp &prop = m_input.text_configuration.font_item.prop; - auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; - if (!m_input.font_file.has_value()) - m_result = priv::create_default_mesh(); - else - m_result = priv::create_mesh(text, m_input.font_file, prop, was_canceled); - if (m_result.its.empty()) - m_result = priv::create_default_mesh(); + if (!priv::check(m_input)) + throw std::runtime_error("Bad input data for EmbossCreateObjectJob."); + auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; + m_result = priv::create_mesh(m_input, was_canceled, ctl); if (was_canceled()) return; // Create new object @@ -213,9 +247,18 @@ void EmbossCreateObjectJob::process(Ctl &ctl) m_transformation = Transform3d(tt); } -void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &) +void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) { - if (canceled) return; + // doesn't care about exception when process was canceled by user + if (canceled) { + eptr = nullptr; + return; + } + if (priv::process(eptr)) return; + + // only for sure + if (m_result.empty()) + return priv::create_message(_u8L("Can't create empty object.")); GUI_App &app = wxGetApp(); Plater *plater = app.plater(); @@ -246,36 +289,38 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &) EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input) : m_input(std::move(input)) { - assert(m_input.cancel != nullptr); - assert(m_input.font_file.has_value()); - assert(!m_input.text_configuration.text.empty()); - assert(!m_input.text_configuration.fix_3mf_tr.has_value()); + assert(priv::check(m_input, true)); } void EmbossUpdateJob::process(Ctl &ctl) { + if (!priv::check(m_input)) + throw std::runtime_error("Bad input data for EmbossUpdateJob."); + auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool { if (cancel->load()) return true; return ctl.was_canceled(); }; - - // check if exist valid font - if (!m_input.font_file.has_value()) return; - - const TextConfiguration &cfg = m_input.text_configuration; - m_result = priv::create_mesh(cfg.text.c_str(), m_input.font_file, - cfg.font_item.prop, was_canceled); - if (m_result.its.empty()) return; + m_result = priv::try_create_mesh(m_input, m_input.font_file, was_canceled); if (was_canceled()) return; + if (m_result.its.empty()) + throw priv::EmbossJobException( + _u8L("Created text volume is empty. Change text or " + "font.").c_str()); // center triangle mesh Vec3d shift = m_result.bounding_box().center(); m_result.translate(-shift.cast()); } -void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &) +void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &eptr) { - if (canceled || m_input.cancel->load()) return; + // doesn't care about exception when process was canceled by user + if (canceled || m_input.cancel->load()) { + eptr = nullptr; + return; + } + if (priv::process(eptr)) return; priv::update_volume(std::move(m_result), m_input); } @@ -283,12 +328,14 @@ void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &) /// Cut Surface UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input) : m_input(std::move(input)) -{} +{ + assert(priv::check(m_input, true)); +} void UseSurfaceJob::process(Ctl &ctl) { - // font face with glyph cache - if (!m_input.font_file.has_value()) return; - + if (!priv::check(m_input)) + throw std::runtime_error("Bad input data for UseSurfaceJob."); + // check cancelation of process auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool { if (cancel->load()) return true; @@ -299,73 +346,169 @@ void UseSurfaceJob::process(Ctl &ctl) { const char *text = tc.text.c_str(); const FontProp &fp = tc.font_item.prop; ExPolygons shapes = Emboss::text2shapes(m_input.font_file, text, fp); - if (shapes.empty()) return; - if (shapes.front().contour.empty()) return; + if (shapes.empty() || shapes.front().contour.empty()) + throw priv::EmbossJobException( + _u8L("Font doesn't have any shape for given text.").c_str()); + if (was_canceled()) return; BoundingBox bb = get_extents(shapes); + // TODO: merge input sources somehow + const UseSurfaceData::ModelSource &source = m_input.sources[0]; - Transform3d mesh_tr_inv = m_input.mesh_tr.inverse(); - Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; - Transform3d emboss_tr = cut_projection_tr.inverse(); - BoundingBoxf3 mesh_bb_tr = m_input.mesh_bb.transformed(emboss_tr); + Transform3d mesh_tr_inv = source.tr.inverse(); + Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; + Transform3d emboss_tr = cut_projection_tr.inverse(); + BoundingBoxf3 mesh_bb_tr = source.bb.transformed(emboss_tr); std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()}; const Emboss::FontFile &ff = *m_input.font_file.font_file; double shape_scale = priv::get_shape_scale(fp, ff); - auto cut_projection = priv::create_projection_for_cut(cut_projection_tr, - shape_scale, bb, - z_range); - if (cut_projection == nullptr) return; + Emboss::OrthoProject cut_projection = priv::create_projection_for_cut( + cut_projection_tr, shape_scale, bb, z_range); // Use CGAL to cut surface from triangle mesh - SurfaceCut cut = cut_surface(m_input.mesh_its, shapes, *cut_projection); - if (cut.empty()) return; + SurfaceCut cut = cut_surface(source.its, shapes, cut_projection); + if (cut.empty()) + throw priv::EmbossJobException( + _u8L("There is no valid surface for text projection.").c_str()); if (was_canceled()) return; // !! Projection needs to transform cut - auto projection = priv::create_emboss_projection(m_input.is_outside, fp.emboss, emboss_tr, cut); - if (projection == nullptr) return; + Emboss::OrthoProject3f projection = priv::create_emboss_projection( + m_input.is_outside, fp.emboss, emboss_tr, cut); + + indexed_triangle_set new_its = cut2model(cut, projection); + assert(!new_its.empty()); - indexed_triangle_set new_its = cut2model(cut, *projection); if (was_canceled()) return; //its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug m_result = TriangleMesh(std::move(new_its)); } -void UseSurfaceJob::finalize(bool canceled, std::exception_ptr &) +void UseSurfaceJob::finalize(bool canceled, std::exception_ptr &eptr) { - if (canceled || m_input.cancel->load()) return; + // doesn't care about exception when process was canceled by user + if (canceled || m_input.cancel->load()) { + eptr = nullptr; + return; + } + if (priv::process(eptr)) return; priv::update_volume(std::move(m_result), m_input); } //////////////////////////// /// private namespace implementation +bool priv::check(const EmbossDataBase &input, bool check_fontfile){ + bool res = true; + if (check_fontfile) { + assert(input.font_file.has_value()); + res &= input.font_file.has_value(); + } + assert(!input.text_configuration.fix_3mf_tr.has_value()); + res &= !input.text_configuration.fix_3mf_tr.has_value(); + assert(!input.text_configuration.text.empty()); + res &= !input.text_configuration.text.empty(); + assert(!input.volume_name.empty()); + res &= !input.volume_name.empty(); + return res; +} +bool priv::check(const EmbossDataCreateVolume &input, bool is_main_thread) { + bool check_fontfile = false; + bool res = check((EmbossDataBase) 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(input.object_idx < wxGetApp().model().objects.size()); + assert(input.screen_coor.x() >= 0.); + res &= input.screen_coor.x() >= 0.; + assert(input.screen_coor.y() >= 0.); + res &= input.screen_coor.y() >= 0.; + return res; +} +bool priv::check(const EmbossDataCreateObject &input) { + bool check_fontfile = false; + bool res = check((EmbossDataBase) input, check_fontfile); + assert(input.screen_coor.x() >= 0.); + res &= input.screen_coor.x() >= 0.; + assert(input.screen_coor.y() >= 0.); + res &= input.screen_coor.y() >= 0.; + assert(input.bed_shape.size() >= 3); // at least triangle + res &= input.bed_shape.size() >= 3; + return res; +} +bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){ + bool res = check((EmbossDataBase) input); + assert(input.volume_id.id >= 0); + res &= input.volume_id.id >= 0; + if (is_main_thread) + assert(get_volume(wxGetApp().model().objects, input.volume_id) != nullptr); + assert(input.cancel != nullptr); + res &= input.cancel != nullptr; + if (is_main_thread) + assert(!input.cancel->load()); + return res; +} +bool priv::check(const UseSurfaceData &input, bool is_main_thread){ + bool res = check((EmbossDataUpdate) input, is_main_thread); + assert(!input.sources.empty()); + res &= !input.sources.empty(); + return res; +} + template -TriangleMesh priv::create_mesh(const char *text, - Emboss::FontFileWithCache &font, - const FontProp &font_prop, - Fnc was_canceled) +TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFileWithCache &font, Fnc was_canceled) { + const TextConfiguration &tc = input.text_configuration; + const char *text = tc.text.c_str(); + const FontProp &prop = tc.font_item.prop; + assert(font.has_value()); if (!font.has_value()) return {}; - ExPolygons shapes = Emboss::text2shapes(font, text, font_prop); + ExPolygons shapes = Emboss::text2shapes(font, text, prop); if (shapes.empty()) return {}; if (was_canceled()) return {}; - const auto &cn = font_prop.collection_number; + const auto &cn = prop.collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; assert(font_index < font.font_file->infos.size()); int unit_per_em = font.font_file->infos[font_index].unit_per_em; - float scale = font_prop.size_in_mm / unit_per_em; - float depth = font_prop.emboss / scale; + float scale = prop.size_in_mm / unit_per_em; + float depth = prop.emboss / scale; auto projectZ = std::make_unique(depth); Emboss::ProjectScale project(std::move(projectZ), scale); if (was_canceled()) return {}; return TriangleMesh(Emboss::polygons2model(shapes, project)); } +template +TriangleMesh priv::create_mesh(EmbossDataBase &input, Fnc was_canceled, Job::Ctl& ctl) +{ + // It is neccessary to create some shape + // Emboss text window is opened by creation new emboss text object + TriangleMesh result; + if (input.font_file.has_value()) { + result = try_create_mesh(input, input.font_file, was_canceled); + if (was_canceled()) return {}; + } + + if (result.its.empty()) { + result = priv::create_default_mesh(); + if (was_canceled()) return {}; + // only info + ctl.call_on_main_thread([]() { + create_message(_u8L("It is used default volume for embossed " + "text, try to change text or font for fix it.")); + }); + } + + assert(!result.its.empty()); + return result; +} + TriangleMesh priv::create_default_mesh() { // When cant load any font use default object loaded from file @@ -382,7 +525,8 @@ void priv::update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data) { // for sure that some object will be created - if (mesh.its.empty()) return; + if (mesh.its.empty()) + return priv::create_message("Empty mesh can't be created."); GUI_App & app = wxGetApp(); // may be move to input Plater * plater = app.plater(); @@ -394,14 +538,7 @@ void priv::update_volume(TriangleMesh &&mesh, std::string snap_name = GUI::format(_L("Text: %1%"), data.text_configuration.text); Plater::TakeSnapshot snapshot(plater, snap_name, UndoRedo::SnapshotType::GizmoAction); - - auto get_volume = [&model = plater->model()](const ObjectID &volume_id)->ModelVolume *{ - for (ModelObject* obj : model.objects) - for (ModelVolume* vol : obj->volumes) - if (vol->id() == volume_id) return vol; - return nullptr; - }; - ModelVolume *volume = get_volume(data.volume_id); + ModelVolume *volume = get_volume(plater->model().objects, data.volume_id); // could appear when user delete edited volume if (volume == nullptr) return; @@ -446,7 +583,17 @@ void priv::update_volume(TriangleMesh &&mesh, canvas->reload_scene(refresh_immediately); } -static double priv::get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff) +ModelVolume *priv::get_volume(ModelObjectPtrs &objects, + const ObjectID &volume_id) +{ + for (ModelObject *obj : objects) + for (ModelVolume *vol : obj->volumes) + if (vol->id() == volume_id) return vol; + return nullptr; +}; + + +double priv::get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff) { const auto &cn = fp.collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; @@ -456,7 +603,7 @@ static double priv::get_shape_scale(const FontProp &fp, const Emboss::FontFile & return scale * Emboss::SHAPE_SCALE; } -std::unique_ptr priv::create_projection_for_cut( +Emboss::OrthoProject priv::create_projection_for_cut( Transform3d tr, double shape_scale, const BoundingBox &shape_bb, @@ -487,10 +634,10 @@ std::unique_ptr priv::create_projection_for_cut( Vec2d move = -(shape_bb.max + shape_bb.min).cast() / 2.; //Vec2d move = -shape_bb.center().cast(); // not precisse tr.translate(Vec3d(move.x(), move.y(), 0.)); - return std::make_unique(tr, project_direction); + return Emboss::OrthoProject(tr, project_direction); } -std::unique_ptr priv::create_emboss_projection( +Emboss::OrthoProject3f priv::create_emboss_projection( bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut) { // Offset of clossed side to model @@ -500,5 +647,23 @@ std::unique_ptr priv::create_emboss_projection( back_move = -((is_outside) ? surface_offset : emboss); its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move))); Vec3f from_front_to_back(0.f, 0.f, back_move - front_move); - return std::make_unique(from_front_to_back); + return Emboss::OrthoProject3f(from_front_to_back); +} + +bool priv::process(std::exception_ptr &eptr) { + if (!eptr) return false; + try { + std::rethrow_exception(eptr); + } catch (priv::EmbossJobException &e) { + create_message(e.what()); + eptr = nullptr; + } + return true; +} + +#include + +void priv::create_message(const std::string &message) { + wxMessageBox(wxString(message), _L("Issue during embossing the text."), + wxOK | wxICON_WARNING); } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index e0a7aee3d..9f7ed6707 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -33,6 +33,7 @@ struct EmbossDataBase /// /// 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 EmbossDataCreateVolume : public EmbossDataBase { @@ -57,6 +58,7 @@ struct EmbossDataCreateVolume : public EmbossDataBase /// /// Create new TextVolume on the surface of ModelObject /// Should not be stopped +/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!! /// class EmbossCreateVolumeJob : public Job { @@ -67,7 +69,7 @@ class EmbossCreateVolumeJob : public Job public: EmbossCreateVolumeJob(EmbossDataCreateVolume&& input); void process(Ctl &ctl) override; - void finalize(bool canceled, std::exception_ptr &) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; }; /// @@ -99,7 +101,7 @@ class EmbossCreateObjectJob : public Job public: EmbossCreateObjectJob(EmbossDataCreateObject&& input); void process(Ctl &ctl) override; - void finalize(bool canceled, std::exception_ptr &) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; }; /// @@ -141,7 +143,7 @@ public: /// NOTE: Be carefull it doesn't care about /// time between finished process and started finalize part. /// unused - void finalize(bool canceled, std::exception_ptr &) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; }; /// @@ -157,13 +159,28 @@ struct UseSurfaceData : public EmbossDataUpdate // False (engraved).. move into object bool is_outside; - // IMPROVE: copy of source mesh tringles - // copy could slow down on big meshes - indexed_triangle_set mesh_its; - // Transformation of volume inside of object - Transform3d mesh_tr; - // extract bounds for projection - BoundingBoxf3 mesh_bb; + struct ModelSource + { + // IMPROVE: copy of source mesh tringles + // copy could slow down on big meshes + // but proccessing on thread need it + indexed_triangle_set its; + // Transformation of volume inside of object + Transform3d tr; + // extract bounds for projection + BoundingBoxf3 bb; + }; + using ModelSources = std::vector; + ModelSources sources; + + //// IMPROVE: copy of source mesh tringles + //// copy could slow down on big meshes + //// but proccess on thread need it + //indexed_triangle_set object_volumes; + //// Transformation of volume inside of object + //Transform3d mesh_tr; + //// extract bounds for projection + //BoundingBoxf3 mesh_bb; }; /// @@ -178,7 +195,7 @@ public: // move params to private variable UseSurfaceJob(UseSurfaceData &&input); void process(Ctl &ctl) override; - void finalize(bool canceled, std::exception_ptr &) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; }; } // namespace Slic3r::GUI