Inform user about unsuccessfull cut surface from job
This commit is contained in:
parent
ff604785f6
commit
4af976e19c
@ -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<Job> 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<UseSurfaceJob>(std::move(surface_data));
|
||||
} else {
|
||||
job = std::make_unique<EmbossUpdateJob>(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
|
||||
|
@ -116,12 +116,6 @@ private:
|
||||
void do_translate(const Vec3d& relative_move);
|
||||
void do_rotate(float relative_z_angle);
|
||||
|
||||
/// <summary>
|
||||
/// Choose valid source Volume to project on(cut surface from).
|
||||
/// </summary>
|
||||
/// <returns>ModelVolume to project on</returns>
|
||||
const ModelVolume *get_volume_to_cut_surface_from();
|
||||
|
||||
/// <summary>
|
||||
/// Reversible input float with option to restor default value
|
||||
/// TODO: make more general, static and move to ImGuiWrapper
|
||||
|
@ -22,20 +22,35 @@ using namespace GUI;
|
||||
|
||||
// private namespace
|
||||
namespace priv{
|
||||
// <summary>
|
||||
/// Create mesh from text
|
||||
/// <summary>
|
||||
/// Assert check of inputs data
|
||||
/// </summary>
|
||||
/// <param name="text">Text to convert on mesh</param>
|
||||
/// <param name="font">Define shape of characters.
|
||||
/// NOTE: Can't be const cache glyphs</param>
|
||||
/// <param name="font_prop">Property of font</param>
|
||||
/// <param name="was_canceled">Lambda returning bool to check if process was canceled</param>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
|
||||
// <summary>
|
||||
/// Try to create mesh from text
|
||||
/// </summary>
|
||||
/// <param name="input">Text to convert on mesh
|
||||
/// + Shape of characters + Property of font</param>
|
||||
/// <param name="font">Font file with cache
|
||||
/// NOTE: Cache glyphs is changed</param>
|
||||
/// <param name="was_canceled">To check if process was canceled</param>
|
||||
/// <returns>Triangle mesh model</returns>
|
||||
template<typename Fnc>
|
||||
static TriangleMesh create_mesh(const char *text,
|
||||
static TriangleMesh try_create_mesh(const EmbossDataBase &input,
|
||||
Emboss::FontFileWithCache &font,
|
||||
const FontProp &font_prop,
|
||||
Fnc was_canceled);
|
||||
template<typename Fnc>
|
||||
static TriangleMesh create_mesh(EmbossDataBase &input,
|
||||
Fnc was_canceled,
|
||||
Job::Ctl &ctl);
|
||||
|
||||
/// <summary>
|
||||
/// Create default mesh for embossed text
|
||||
/// </summary>
|
||||
@ -49,6 +64,14 @@ static TriangleMesh create_default_mesh();
|
||||
/// <param name="data">Text configuration, ...</param>
|
||||
static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data);
|
||||
|
||||
/// <summary>
|
||||
/// Select Volume from objects
|
||||
/// </summary>
|
||||
/// <param name="objects">All objects in scene</param>
|
||||
/// <param name="volume_id">Identifier of volume in object</param>
|
||||
/// <returns>Pointer to volume when exist otherwise nullptr</returns>
|
||||
static ModelVolume *get_volume(ModelObjectPtrs &objects,
|
||||
const ObjectID &volume_id);
|
||||
/// <summary>
|
||||
/// extract scale in 2d
|
||||
/// </summary>
|
||||
@ -65,7 +88,7 @@ static double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff);
|
||||
/// <param name="shape_bb">Bounding box 2d of shape to center result</param>
|
||||
/// <param name="z_range">Bounding box 3d of model volume for projection ranges</param>
|
||||
/// <returns>Orthogonal cut_projection</returns>
|
||||
static std::unique_ptr<Emboss::IProjection> create_projection_for_cut(
|
||||
static Emboss::OrthoProject create_projection_for_cut(
|
||||
Transform3d tr,
|
||||
double shape_scale,
|
||||
const BoundingBox &shape_bb,
|
||||
@ -79,26 +102,31 @@ static std::unique_ptr<Emboss::IProjection> create_projection_for_cut(
|
||||
/// <param name="tr">Text voliume transformation inside object</param>
|
||||
/// <param name="cut">Cutted surface from model</param>
|
||||
/// <returns>Projection</returns>
|
||||
static std::unique_ptr<Emboss::IProject3f> 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
|
||||
@ -110,24 +138,35 @@ 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();
|
||||
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);
|
||||
|
||||
// set a default extruder value, since user can't add it manually
|
||||
@ -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<float>());
|
||||
}
|
||||
|
||||
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,11 +328,13 @@ 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 {
|
||||
@ -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 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 = m_input.mesh_bb.transformed(emboss_tr);
|
||||
BoundingBoxf3 mesh_bb_tr = source.bb.transformed(emboss_tr);
|
||||
std::pair<float, float> 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<typename Fnc>
|
||||
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<Emboss::ProjectZ>(depth);
|
||||
Emboss::ProjectScale project(std::move(projectZ), scale);
|
||||
if (was_canceled()) return {};
|
||||
return TriangleMesh(Emboss::polygons2model(shapes, project));
|
||||
}
|
||||
|
||||
template<typename Fnc>
|
||||
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<Emboss::IProjection> 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<Emboss::IProjection> priv::create_projection_for_cut(
|
||||
Vec2d move = -(shape_bb.max + shape_bb.min).cast<double>() / 2.;
|
||||
//Vec2d move = -shape_bb.center().cast<double>(); // not precisse
|
||||
tr.translate(Vec3d(move.x(), move.y(), 0.));
|
||||
return std::make_unique<Emboss::OrthoProject>(tr, project_direction);
|
||||
return Emboss::OrthoProject(tr, project_direction);
|
||||
}
|
||||
|
||||
std::unique_ptr<Emboss::IProject3f> 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<Emboss::IProject3f> 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<Emboss::OrthoProject3f>(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 <wx/msgdlg.h>
|
||||
|
||||
void priv::create_message(const std::string &message) {
|
||||
wxMessageBox(wxString(message), _L("Issue during embossing the text."),
|
||||
wxOK | wxICON_WARNING);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ struct EmbossDataBase
|
||||
/// <summary>
|
||||
/// Hold neccessary data to create ModelVolume in job
|
||||
/// Volume is created on the surface of existing volume in object.
|
||||
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
|
||||
/// </summary>
|
||||
struct EmbossDataCreateVolume : public EmbossDataBase
|
||||
{
|
||||
@ -57,6 +58,7 @@ struct EmbossDataCreateVolume : public EmbossDataBase
|
||||
/// <summary>
|
||||
/// Create new TextVolume on the surface of ModelObject
|
||||
/// Should not be stopped
|
||||
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
|
||||
/// </summary>
|
||||
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;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -141,7 +143,7 @@ public:
|
||||
/// NOTE: Be carefull it doesn't care about
|
||||
/// time between finished process and started finalize part.</param>
|
||||
/// <param name="">unused</param>
|
||||
void finalize(bool canceled, std::exception_ptr &) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -157,13 +159,28 @@ struct UseSurfaceData : public EmbossDataUpdate
|
||||
// False (engraved).. move into object
|
||||
bool is_outside;
|
||||
|
||||
struct ModelSource
|
||||
{
|
||||
// IMPROVE: copy of source mesh tringles
|
||||
// copy could slow down on big meshes
|
||||
indexed_triangle_set mesh_its;
|
||||
// but proccessing on thread need it
|
||||
indexed_triangle_set its;
|
||||
// Transformation of volume inside of object
|
||||
Transform3d mesh_tr;
|
||||
Transform3d tr;
|
||||
// extract bounds for projection
|
||||
BoundingBoxf3 mesh_bb;
|
||||
BoundingBoxf3 bb;
|
||||
};
|
||||
using ModelSources = std::vector<ModelSource>;
|
||||
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;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user