Separate create volume from edit

This commit is contained in:
Filip Sykala 2021-11-25 15:26:45 +01:00
parent 456952325f
commit 1e719bab26
7 changed files with 2769 additions and 112 deletions

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,10 @@ Points CameraUtils::project(const Camera & camera,
Slic3r::Polygon CameraUtils::create_hull2d(const Camera & camera,
const GLVolume &volume)
{
const indexed_triangle_set &its = volume.convex_hull()->its;
// TODO: fix Negative volume doesnt have convex hull
const TriangleMesh *hull = volume.convex_hull();
assert(hull != nullptr);
const indexed_triangle_set &its = hull->its;
const Transform3d & trafoMat =
volume.get_instance_transformation().get_matrix() *
volume.get_volume_transformation().get_matrix();

View File

@ -490,7 +490,7 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty
mng.open_gizmo(GLGizmosManager::Emboss)) &&
type != ModelVolumeType::INVALID) {
GLGizmoEmboss *emboss = dynamic_cast<GLGizmoEmboss *>(mng.get_current());
if (emboss != nullptr) emboss->set_volume_type(type);
if (emboss != nullptr) emboss->create_volume(type);
}
};

View File

@ -20,6 +20,7 @@
#include "libslic3r/ClipperUtils.hpp" // union_ex
#include "libslic3r/AppConfig.hpp" // store/load font list
#include "libslic3r/MapUtils.hpp"
#include "libslic3r/Format/OBJ.hpp" // load obj file for default object
#include "imgui/imgui_stdlib.h" // using std::string for inputs
#include "nanosvg/nanosvg.h" // load SVG file
@ -189,6 +190,47 @@ public:
#endif // __linux__
class Priv
{
public:
Priv() = delete;
struct EmbossVolume
{
TriangleMesh mesh;
std::string name;
TextConfiguration cfg;
ModelVolumeType type;
size_t object_idx;
EmbossVolume(TriangleMesh mesh,
std::string name,
TextConfiguration cfg)
: mesh(mesh)
, name(name)
, cfg(cfg)
, type(ModelVolumeType::MODEL_PART)
, object_idx(0) // not used
{}
EmbossVolume(TriangleMesh mesh,
std::string name,
TextConfiguration cfg,
ModelVolumeType type,
size_t object_idx)
: mesh(mesh)
, name(name)
, cfg(cfg)
, type(type)
, object_idx(object_idx)
{}
};
// it is called after draw
static void create_emboss_object(EmbossVolume *data);
static void create_emboss_volume(EmbossVolume *data);
};
} // namespace Slic3r
using namespace Slic3r;
@ -204,12 +246,9 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
, m_job(std::make_unique<EmbossJob>())
{
// TODO: add suggestion to use https://fontawesome.com/
// (copy & paste) unicode symbols from web
// (copy & paste) unicode symbols from web
}
GLGizmoEmboss::~GLGizmoEmboss() { m_job->stop(); }
void GLGizmoEmboss::set_fine_position()
{
const Selection & selection = m_parent.get_selection();
@ -264,11 +303,18 @@ static void draw_fine_position(const Selection &selection)
}
#endif // ALLOW_DEBUG_MODE
void GLGizmoEmboss::set_volume_type(ModelVolumeType volume_type)
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
{
if (m_volume == nullptr) return;
m_volume->set_type(volume_type);
m_parent.reload_scene(true);
if (!m_is_initialized) initialize();
const Selection &selection = m_parent.get_selection();
if(selection.is_empty()) return;
set_default_configuration();
auto data = new Priv::EmbossVolume(m_default_mesh, create_volume_name(),
create_configuration(), volume_type,
selection.get_object_idx());
wxGetApp().plater()->CallAfter(
[data]() { Priv::create_emboss_volume(data); });
}
bool GLGizmoEmboss::on_init()
@ -287,7 +333,6 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
{
check_selection();
ImVec2 min_window_size = m_gui_cfg->draw_advanced ?
m_gui_cfg->minimal_window_size_with_advance :
m_gui_cfg->minimal_window_size;
@ -333,32 +378,113 @@ void GLGizmoEmboss::on_set_state()
return;
}
m_volume = nullptr;
m_job->stop();
m_job->join(); // free thread resource
remove_notification_not_valid_font();
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
if (!m_is_initialized) initialize();
set_fine_position();
const Selection &selection = m_parent.get_selection();
bool create_new_object = selection.is_empty();
// When add Text on empty plate, Create new object with volume
if (m_parent.get_selection().is_empty()) {
if (!create_default_model_object())
GLGizmoBase::m_state = GLGizmoBase::Off;
if (create_new_object) {
set_default_configuration();
// data are owen by lambda
auto data = new Priv::EmbossVolume(m_default_mesh,
create_volume_name(),
create_configuration());
wxGetApp().plater()->CallAfter([data]() {
Priv::create_emboss_object(data);
});
// gizmo will open when successfuly create new object
GLGizmoBase::m_state = GLGizmoBase::Off;
return;
}
// Try set selected volume
if (!load_configuration(get_selected_volume())) {
// No volume with text selected, create new one
set_default_configuration();
process();
}
// Try(when exist) set configuration by volume
load_configuration(get_selected_volume());
// change position of just opened emboss window
set_fine_position();
// when open by hyperlink it needs to show up
m_parent.reload_scene(true);
}
}
void Priv::create_emboss_object(EmbossVolume *data)
{
ScopeGuard sg([data]() { delete data; });
GUI_App & app = wxGetApp();
Plater * plater = app.plater();
ObjectList * obj_list = app.obj_list();
GLCanvas3D * canvas = plater->canvas3D();
GLGizmosManager &manager = canvas->get_gizmos_manager();
plater->take_snapshot(_L("Add Emboss text object"));
// Create new object and change selection
bool center = true;
obj_list->load_mesh_object(data->mesh, data->name, center, &data->cfg);
// new object successfuly added so open gizmo
assert(manager.get_current_type() != GLGizmosManager::Emboss);
manager.open_gizmo(GLGizmosManager::Emboss);
// redraw scene
canvas->reload_scene(true);
// Gizmo is not open during time of creation object
// When cursor move and no one object is selected than Manager::reset_all()
}
void Priv::create_emboss_volume(EmbossVolume* data)
{
ScopeGuard sg([data]() { delete data; });
GUI_App & app = wxGetApp();
Plater * plater = app.plater();
ObjectList *obj_list = app.obj_list();
GLCanvas3D *canvas = plater->canvas3D();
size_t object_idx = data->object_idx;
ModelVolumeType type = data->type;
// create new volume inside of object
Model &model = plater->model();
if (model.objects.size() <= object_idx) return;
ModelObject *obj = model.objects[object_idx];
ModelVolume *volume = obj->add_volume(std::move(data->mesh));
// set a default extruder value, since user can't add it manually
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
// do not allow model reload from disk
volume->source.is_from_builtin_objects = true;
volume->set_type(type);
volume->name = data->name;
volume->text_configuration = data->cfg;
// update volume name in object list
// updata selection after new volume added
// change name of volume in right panel
// 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((int)object_idx, add_to_selection);
if (!sel.IsEmpty()) obj_list->select_item(sel.front());
// update printable state on canvas
if (type == ModelVolumeType::MODEL_PART)
canvas->update_instance_printable_state_for_object(object_idx);
obj_list->selection_changed();
// redraw scene
canvas->reload_scene(true);
}
void GLGizmoEmboss::initialize()
{
if (m_is_initialized) return;
@ -389,16 +515,37 @@ void GLGizmoEmboss::initialize()
load_font_list();
m_font_selected = 0;
// try to load valid font
m_font_selected = 0;
bool is_font_loaded = load_font();
while (!is_font_loaded && !m_font_list.empty()) {
// can't load so erase it from list
m_font_list.erase(m_font_list.begin() + m_font_selected);
m_font_selected = 0; // select first
is_font_loaded = load_font();
m_font_list.erase(m_font_list.begin());
is_font_loaded = load_font();
}
set_default_configuration();
// create default mesh to faster add new volume
// solve state when no font loaded
if (is_font_loaded) {
// create default
ExPolygons shapes = Emboss::text2shapes(*m_font, m_text.c_str(), m_font_prop);
float scale = m_font_prop.size_in_mm / m_font->ascent;
float depth = m_font_prop.emboss / scale;
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth);
Emboss::ProjectScale project(std::move(projectZ), scale);
m_default_mesh = TriangleMesh(Emboss::polygons2model(shapes, project));
} else {
// When cant load any font use default object loaded from file
std::string path = Slic3r::resources_dir() +
"/data/embossed_text.stl";
if (!load_obj(path.c_str(), &m_default_mesh)) {
// when can't load mesh use cube
m_default_mesh = TriangleMesh(its_make_cube(36., 4., 2.5));
}
}
}
FontList GLGizmoEmboss::create_default_font_list() {
@ -430,10 +577,10 @@ void GLGizmoEmboss::check_selection()
if (m_volume != nullptr) ImGui::ClearActiveID();
// is select embossed volume?
if (load_configuration(vol)) {
if (load_configuration(vol))
// successfull load volume for editing
return;
}
// behave like adding new text
m_volume = nullptr;
@ -491,6 +638,8 @@ void GLGizmoEmboss::close()
{
// close gizmo == open it again
m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss);
m_job->stop();
m_job->join(); // free thread resource
}
void GLGizmoEmboss::draw_window()
@ -718,43 +867,6 @@ void GLGizmoEmboss::draw_advanced()
#endif // ALLOW_DEBUG_MODE
}
bool GLGizmoEmboss::create_default_model_object()
{
set_default_configuration();
// Is created default model?
if (process()) return true;
// can't create object,
// e.g. selected font don't have letter for "Emboss text"
// try select another font
for (size_t font_index = 0; font_index < m_font_list.size();
++font_index) {
if (!load_font(font_index)) continue;
// Is fixed by change to font from font list?
if (process()) return true;
}
// try add system font and use it
size_t font_index = m_font_list.size();
m_font_list.push_back(WxFontUtils::get_os_font());
if (!load_font(font_index)) {
// TODO: Solve wrong os font !!!
return false;
}
// Is fixed by change font to os font?
if (process()) return true;
// try change default text
// Do NOT translate it!
m_text = u8"text";
// Is fixed by change translation of Emboss text?
if (process()) return true;
// Bad os font can't process "text" with os default font
return false;
}
bool GLGizmoEmboss::load_font(size_t font_index)
{
std::swap(font_index, m_font_selected);

View File

@ -27,9 +27,8 @@ class GLGizmoEmboss : public GLGizmoBase
{
public:
GLGizmoEmboss(GLCanvas3D& parent);
virtual ~GLGizmoEmboss();
void set_volume_type(ModelVolumeType volume_type);
void create_volume(ModelVolumeType volume_type);
void set_fine_position();
protected:
virtual bool on_init() override;
@ -57,8 +56,6 @@ private:
void draw_text_input();
void draw_advanced();
bool create_default_model_object();
bool load_font();
// try to set font_index
bool load_font(size_t font_index);
@ -120,6 +117,7 @@ private:
std::shared_ptr<Emboss::Font> m_font;
std::string m_text;
FontProp m_font_prop;
TriangleMesh m_default_mesh; // when add new text this shape is used
// thread to process object on change text
std::unique_ptr<EmbossJob> m_job;

View File

@ -83,58 +83,39 @@ void EmbossJob::process(std::unique_ptr<EmbossData> input, StopCondition is_stop
void Priv::finalize(const EmbossData &input, const indexed_triangle_set &result)
{
GUI_App & app = wxGetApp(); // may be move to input
Plater * plater = app.plater();
GLCanvas3D * canvas = plater->canvas3D();
GLGizmosManager &manager= canvas->get_gizmos_manager();
// TODO: Solve gizmo with empty selection first
// Check emboss gizmo is still open
//if (manager.get_current_type() != GLGizmosManager::Emboss) return;
// it is sad, but there is no move constructor --> copy
TriangleMesh tm(std::move(result));
// center triangle mesh
Vec3d shift = tm.bounding_box().center();
tm.translate(-shift.cast<float>());
GUI_App & app = wxGetApp();
Plater * plater = app.plater();
GLCanvas3D * canvas = plater->canvas3D();
const std::string &name = input.volume_name;
const std::string &name = input.volume_name;
plater->take_snapshot(_L("Emboss text") + ": " + name);
ModelVolume *volume = input.volume_ptr;
if (volume == nullptr) {
// decide to add as volume or new object
if (input.object_idx < 0) {
// create new object
app.obj_list()->load_mesh_object(tm, name, true, &input.text_configuration);
app.mainframe->update_title();
// TODO: find Why ???
// load mesh cause close gizmo, on windows but not on linux
// Open gizmo again when it is closed
GLGizmosManager &mng = canvas->get_gizmos_manager();
if (mng.get_current_type() != GLGizmosManager::Emboss)
mng.open_gizmo(GLGizmosManager::Emboss);
return;
} else {
// create new volume inside of object
Model &model = plater->model();
if (model.objects.size() <= input.object_idx) return;
ModelObject *obj = model.objects[input.object_idx];
volume = obj->add_volume(std::move(tm));
// set a default extruder value, since user can't add it manually
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
}
} else {
// update volume
volume->set_mesh(std::move(tm));
volume->set_new_unique_id();
volume->calculate_convex_hull();
volume->get_object()->invalidate_bounding_box();
}
assert(volume != nullptr);
// update volume
volume->set_mesh(std::move(tm));
volume->set_new_unique_id();
volume->calculate_convex_hull();
volume->get_object()->invalidate_bounding_box();
volume->name = name;
volume->text_configuration = input.text_configuration;
// update volume name in object list
// updata selection after new volume added
// change name of volume in right panel
select_volume(volume);
//select_volume(volume);
// Job promiss to refresh is not working
canvas->reload_scene(true);
@ -144,19 +125,25 @@ void Priv::select_volume(ModelVolume *volume)
{
if (volume == nullptr) return;
ObjectList *obj_list = wxGetApp().obj_list();
GUI_App & app = wxGetApp();
ObjectList *obj_list = app.obj_list();
GLCanvas3D *canvas = app.plater()->canvas3D();
// 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;
};
const Selection &selection =
wxGetApp().plater()->canvas3D()->get_selection();
wxDataViewItemArray sel =
obj_list->reorder_volumes_and_get_selection(selection.get_object_idx(),
add_to_selection);
const Selection &selection = canvas->get_selection();
int obj_idx = selection.get_object_idx();
wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(obj_idx, add_to_selection);
if (!sel.IsEmpty()) obj_list->select_item(sel.front());
if (volume->type() == ModelVolumeType::MODEL_PART)
// update printable state on canvas
canvas->update_instance_printable_state_for_object((size_t)obj_idx);
obj_list->selection_changed();
}

View File

@ -55,6 +55,8 @@ public:
/// Free thread resources by join thread
/// Be Carefull, it is blocking until join
/// Suggest to call stop() before join
/// Thread join could throw exception
/// https://en.cppreference.com/w/cpp/thread/thread/join
/// </summary>
void join();
protected: