mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-11 00:01:47 +08:00
Emboss on job SVG and Text together
This commit is contained in:
parent
07181b4f37
commit
b6d84fb276
@ -14,7 +14,6 @@
|
||||
#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
|
||||
@ -108,14 +107,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
|
||||
|
||||
// Private namespace with helper function for create volume
|
||||
namespace priv {
|
||||
|
||||
/// <summary>
|
||||
/// Check if volume type is possible use for new text volume
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Type</param>
|
||||
/// <returns>True when allowed otherwise false</returns>
|
||||
static bool is_valid(ModelVolumeType volume_type);
|
||||
|
||||
/// <summary>
|
||||
/// Prepare data for emboss
|
||||
/// </summary>
|
||||
@ -134,7 +125,7 @@ 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;
|
||||
void write(ModelVolume &volume) const override;
|
||||
|
||||
// private:
|
||||
// Keep pointer on Data of font (glyph shapes)
|
||||
@ -143,31 +134,6 @@ struct TextDataBase : public DataBase
|
||||
TextConfiguration text_configuration;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Start job for add object with text into scene
|
||||
/// </summary>
|
||||
/// <param name="emboss_data">Define params of text</param>
|
||||
/// <param name="coor">Screen coordinat, where to create new object laying on bed</param>
|
||||
static void start_create_object_job(std::unique_ptr<DataBase> emboss_data, const Vec2d &coor);
|
||||
|
||||
/// <summary>
|
||||
/// Start job for add new volume on surface of object defined by screen coor
|
||||
/// </summary>
|
||||
/// <param name="emboss_data">Define params of text</param>
|
||||
/// <param name="volume_type">Emboss / engrave</param>
|
||||
/// <param name="screen_coor">Mouse position which define position</param>
|
||||
/// <param name="gl_volume">Volume to find surface for create</param>
|
||||
/// <param name="raycaster">Ability to ray cast to model</param>
|
||||
/// <param name="canvas">Contain already used scene RayCasters</param>
|
||||
/// <returns>True when start creation, False when there is no hit surface by screen coor</returns>
|
||||
static bool start_create_volume_on_surface_job(const FontProp &fp,
|
||||
std::unique_ptr<DataBase> 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()
|
||||
enum class IconType : unsigned {
|
||||
@ -194,69 +160,26 @@ const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType typ
|
||||
static bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false);
|
||||
} // namespace priv
|
||||
|
||||
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
|
||||
bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
|
||||
{
|
||||
if (!priv::is_valid(volume_type)) return;
|
||||
m_style_manager.discard_style_changes();
|
||||
set_default_text();
|
||||
|
||||
std::unique_ptr<DataBase> emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
||||
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
|
||||
return priv::start_create_object_job(std::move(emboss_data), mouse_pos);
|
||||
|
||||
set_default_text();
|
||||
DataBasePtr base = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
||||
Plater *plater_ptr = wxGetApp().plater();
|
||||
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);
|
||||
return start_create_volume(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Emboss, mouse_pos, fp.distance, fp.angle);
|
||||
}
|
||||
|
||||
// Designed for create volume without information of mouse in scene
|
||||
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
|
||||
bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
|
||||
{
|
||||
if (!priv::is_valid(volume_type)) return;
|
||||
m_style_manager.discard_style_changes();
|
||||
set_default_text();
|
||||
|
||||
// select position by camera position and view direction
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
int object_idx = selection.get_object_idx();
|
||||
|
||||
Size s = m_parent.get_canvas_size();
|
||||
Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
|
||||
std::unique_ptr<DataBase> emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
||||
const ModelObjectPtrs &objects = selection.get_model()->objects;
|
||||
// No selected object so create new object
|
||||
if (selection.is_empty() || object_idx < 0 || static_cast<size_t>(object_idx) >= objects.size()) {
|
||||
// create Object on center of screen
|
||||
// when ray throw center of screen not hit bed it create object on center of bed
|
||||
priv::start_create_object_job(std::move(emboss_data), screen_center);
|
||||
return;
|
||||
}
|
||||
|
||||
// create volume inside of selected object
|
||||
Vec2d coor;
|
||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||
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<DataBase> emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
||||
// there is no point on surface so no use of surface will be applied
|
||||
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);
|
||||
}
|
||||
Plater *plater_ptr = wxGetApp().plater();
|
||||
const FontProp &fp = m_style_manager.get_style().prop;
|
||||
return start_create_volume_without_position(plater_ptr, std::move(emboss_data),
|
||||
volume_type, m_raycast_manager, GLGizmosManager::Emboss, fp.distance, fp.angle);
|
||||
}
|
||||
|
||||
bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||
@ -1057,7 +980,7 @@ bool GLGizmoEmboss::process()
|
||||
|
||||
if (use_surface) {
|
||||
// Model to cut surface from.
|
||||
SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume);
|
||||
SurfaceVolumeData::ModelSources sources = create_volume_sources(*m_volume);
|
||||
if (sources.empty()) return false;
|
||||
|
||||
Transform3d text_tr = m_volume->get_matrix();
|
||||
@ -3254,17 +3177,6 @@ bool GLGizmoEmboss::is_text_object(const ModelVolume *text) {
|
||||
// priv namespace implementation
|
||||
///////////////
|
||||
|
||||
bool priv::is_valid(ModelVolumeType volume_type)
|
||||
{
|
||||
if (volume_type == ModelVolumeType::MODEL_PART ||
|
||||
volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
|
||||
volume_type == ModelVolumeType::PARAMETER_MODIFIER)
|
||||
return true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << "Can't create embossed text with this type: " << (int) 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))
|
||||
{
|
||||
@ -3351,50 +3263,6 @@ std::unique_ptr<DataBase> priv::create_emboss_data_base(const std::string &text,
|
||||
return std::make_unique<TextDataBase>(std::move(base), font, std::move(tc));
|
||||
}
|
||||
|
||||
void priv::start_create_object_job(std::unique_ptr<DataBase> emboss_data, const Vec2d &coor) {
|
||||
Plater *plater = wxGetApp().plater();
|
||||
const Camera &camera = plater->get_camera();
|
||||
const Pointfs &bed_shape = plater->build_volume().bed_shape();
|
||||
Worker &worker = plater->get_ui_job_worker();
|
||||
DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape};
|
||||
auto job = std::make_unique<CreateObjectJob>(std::move(data));
|
||||
queue_job(worker, std::move(job));
|
||||
}
|
||||
|
||||
bool priv::start_create_volume_on_surface_job(const FontProp &fp,
|
||||
std::unique_ptr<DataBase> emboss_data,
|
||||
ModelVolumeType volume_type,
|
||||
const Vec2d &screen_coor,
|
||||
const GLVolume &gl_volume,
|
||||
RaycastManager &raycaster,
|
||||
GLCanvas3D &canvas)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Plater* plater = wxGetApp().plater();
|
||||
const Camera &camera = plater->get_camera();
|
||||
std::optional<Transform3d> 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)
|
||||
{
|
||||
const Selection::IndicesList indices = selection.get_volume_idxs();
|
||||
|
@ -39,13 +39,13 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
/// <param name="mouse_pos">Define position of new volume</param>
|
||||
void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
|
||||
bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
|
||||
|
||||
/// <summary>
|
||||
/// Create new text without given position
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
void create_volume(ModelVolumeType volume_type);
|
||||
bool create_volume(ModelVolumeType volume_type);
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/CameraUtils.hpp"
|
||||
#include "slic3r/GUI/Jobs/EmbossJob.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
@ -32,11 +33,11 @@
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Emboss;
|
||||
using namespace Slic3r::GUI;
|
||||
using namespace Slic3r::GUI::Emboss;
|
||||
|
||||
namespace priv {
|
||||
// Variable keep limits for variables
|
||||
static const struct Limits
|
||||
{
|
||||
static const struct Limits{
|
||||
MinMax<float> emboss{0.01f, 1e4f}; // in mm
|
||||
// distance text object from surface
|
||||
MinMax<float> angle{-180.f, 180.f}; // in degrees
|
||||
@ -55,20 +56,34 @@ GLGizmoSVG::GLGizmoSVG(GLCanvas3D &parent)
|
||||
|
||||
// Private functions to create emboss volume
|
||||
namespace priv {
|
||||
|
||||
/// <summary>
|
||||
/// Check if volume type is possible use for new text volume
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Type</param>
|
||||
/// <returns>True when allowed otherwise false</returns>
|
||||
static bool is_valid(ModelVolumeType volume_type);
|
||||
|
||||
/// <summary>
|
||||
/// Open file dialog with svg files
|
||||
/// </summary>
|
||||
/// <returns>File path to svg</returns>
|
||||
static std::string choose_svg_file();
|
||||
|
||||
/// <summary>
|
||||
/// Let user to choose file with (S)calable (V)ector (G)raphics - SVG.
|
||||
/// Than let select contour
|
||||
/// </summary>
|
||||
/// <returns>EmbossShape to create</returns>
|
||||
static EmbossShape select_shape();
|
||||
|
||||
/// <summary>
|
||||
/// Create new embos data
|
||||
/// </summary>
|
||||
/// <param name="cancel">Cancel for previous job</param>
|
||||
/// <returns>Base data for emboss SVG</returns>
|
||||
static DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel);
|
||||
|
||||
/// <summary>
|
||||
/// Create symbol '?' as default shape
|
||||
/// without source file
|
||||
/// with size 2cm
|
||||
/// </summary>
|
||||
/// <returns>Default shape to emboss</returns>
|
||||
static ExPolygons default_shape();
|
||||
|
||||
/// <summary>
|
||||
/// Separate file name from file path.
|
||||
/// String after last delimiter and before last point
|
||||
@ -77,53 +92,39 @@ static std::string choose_svg_file();
|
||||
/// <returns>File name without directory path</returns>
|
||||
static std::string get_file_name(const std::string &file_path);
|
||||
|
||||
/// <summary>
|
||||
/// Create volume name from shape information
|
||||
/// </summary>
|
||||
/// <param name="shape">File path</param>
|
||||
/// <returns>Name for volume</returns>
|
||||
static std::string volume_name(const EmbossShape& shape);
|
||||
|
||||
} // namespace priv
|
||||
|
||||
|
||||
void GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos){
|
||||
std::string path = priv::choose_svg_file();
|
||||
if (path.empty()) return;
|
||||
create_volume(path, volume_type, mouse_pos);
|
||||
}
|
||||
void GLGizmoSVG::create_volume(ModelVolumeType volume_type) {
|
||||
std::string path = priv::choose_svg_file();
|
||||
if (path.empty()) return;
|
||||
create_volume(path, volume_type);
|
||||
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
|
||||
{
|
||||
DataBasePtr base = priv::create_emboss_data_base(m_job_cancel);
|
||||
Plater *plater_ptr = wxGetApp().plater();
|
||||
return start_create_volume(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Svg, mouse_pos);
|
||||
}
|
||||
|
||||
void GLGizmoSVG::create_volume(const std::string &svg_file_path, ModelVolumeType volume_type, const Vec2d &mouse_pos) {
|
||||
std::string name = priv::get_file_name(svg_file_path);
|
||||
NSVGimage *image = nsvgParseFromFile(svg_file_path.c_str(), "mm", 96.0f);
|
||||
ExPolygons polys = NSVGUtils::to_ExPolygons(image);
|
||||
nsvgDelete(image);
|
||||
|
||||
BoundingBox bb;
|
||||
for (const auto &p : polys)
|
||||
bb.merge(p.contour.points);
|
||||
|
||||
double scale = 1e-4;
|
||||
auto project = std::make_unique<ProjectScale>(std::make_unique<ProjectZ>(10 / scale), scale);
|
||||
indexed_triangle_set its = polygons2model(polys, *project);
|
||||
// add volume
|
||||
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type)
|
||||
{
|
||||
DataBasePtr base = priv::create_emboss_data_base(m_job_cancel);
|
||||
Plater *plater_ptr = wxGetApp().plater();
|
||||
return start_create_volume_without_position(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Svg);
|
||||
}
|
||||
|
||||
void GLGizmoSVG::create_volume(const std::string &svg_file_path, ModelVolumeType volume_type) {
|
||||
|
||||
bool GLGizmoSVG::is_svg(const ModelVolume &volume) {
|
||||
return volume.emboss_shape.has_value();
|
||||
}
|
||||
|
||||
bool GLGizmoSVG::is_svg(const ModelVolume *volume) {
|
||||
if (volume == nullptr)
|
||||
return false;
|
||||
return volume->emboss_shape.has_value();
|
||||
}
|
||||
|
||||
bool GLGizmoSVG::is_svg_object(const ModelVolume *volume) {
|
||||
if (volume == nullptr) return false;
|
||||
if (!volume->emboss_shape.has_value()) return false;
|
||||
if (volume->type() != ModelVolumeType::MODEL_PART) return false;
|
||||
for (const ModelVolume *v : volume->get_object()->volumes) {
|
||||
if (v == volume) continue;
|
||||
bool GLGizmoSVG::is_svg_object(const ModelVolume &volume) {
|
||||
if (!volume.emboss_shape.has_value()) return false;
|
||||
if (volume.type() != ModelVolumeType::MODEL_PART) return false;
|
||||
for (const ModelVolume *v : volume.get_object()->volumes) {
|
||||
if (v->id() == volume.id()) continue;
|
||||
if (v->type() == ModelVolumeType::MODEL_PART) return false;
|
||||
}
|
||||
return true;
|
||||
@ -423,7 +424,7 @@ void GLGizmoSVG::set_volume_by_selection()
|
||||
ImGuiWrapper::left_inputs();
|
||||
|
||||
// is valid svg volume?
|
||||
if (!is_svg(volume))
|
||||
if (!is_svg(*volume))
|
||||
return reset_volume();
|
||||
|
||||
// cancel previous job
|
||||
@ -527,8 +528,8 @@ void GLGizmoSVG::close()
|
||||
{
|
||||
// close gizmo == open it again
|
||||
auto& mng = m_parent.get_gizmos_manager();
|
||||
if (mng.get_current_type() == GLGizmosManager::Emboss)
|
||||
mng.open_gizmo(GLGizmosManager::Emboss);
|
||||
if (mng.get_current_type() == GLGizmosManager::Svg)
|
||||
mng.open_gizmo(GLGizmosManager::Svg);
|
||||
}
|
||||
|
||||
void GLGizmoSVG::draw_window()
|
||||
@ -543,7 +544,8 @@ void GLGizmoSVG::draw_window()
|
||||
|
||||
void GLGizmoSVG::draw_model_type()
|
||||
{
|
||||
bool is_last_solid_part = is_svg_object(m_volume);
|
||||
assert(m_volume != nullptr);
|
||||
bool is_last_solid_part = is_svg_object(*m_volume);
|
||||
std::string title = _u8L("SVG is to object");
|
||||
if (is_last_solid_part) {
|
||||
ImVec4 color{.5f, .5f, .5f, 1.f};
|
||||
@ -561,7 +563,7 @@ void GLGizmoSVG::draw_model_type()
|
||||
if (ImGui::RadioButton(_u8L("Added").c_str(), type == part))
|
||||
new_type = part;
|
||||
else if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", _u8L("Click to change text into object part.").c_str());
|
||||
ImGui::SetTooltip("%s", _u8L("Change to object's part.").c_str());
|
||||
ImGui::SameLine();
|
||||
|
||||
std::string last_solid_part_hint = _u8L("You can't change a type of the last solid part of the object.");
|
||||
@ -571,11 +573,16 @@ void GLGizmoSVG::draw_model_type()
|
||||
if (is_last_solid_part)
|
||||
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
|
||||
else if (type != negative)
|
||||
ImGui::SetTooltip("%s", _u8L("Click to change part type into negative volume.").c_str());
|
||||
ImGui::SetTooltip("%s", _u8L("Change to negative volume.").c_str());
|
||||
}
|
||||
|
||||
// In simple mode are not modifiers
|
||||
if (wxGetApp().plater()->printer_technology() != ptSLA && wxGetApp().get_mode() != ConfigOptionMode::comSimple) {
|
||||
GUI_App &app = wxGetApp();
|
||||
Plater *plater = app.plater();
|
||||
// SLA printers do not use modifiers
|
||||
bool is_sla = plater->printer_technology() == ptSLA;
|
||||
// In simple mode are not modifiers ?!?
|
||||
bool is_simple = app.get_mode() == ConfigOptionMode::comSimple;
|
||||
if (!is_sla && !is_simple) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton(_u8L("Modifier").c_str(), type == modifier))
|
||||
new_type = modifier;
|
||||
@ -583,13 +590,11 @@ void GLGizmoSVG::draw_model_type()
|
||||
if (is_last_solid_part)
|
||||
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
|
||||
else if (type != modifier)
|
||||
ImGui::SetTooltip("%s", _u8L("Click to change part type into modifier.").c_str());
|
||||
ImGui::SetTooltip("%s", _u8L("Change to modifier.").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_volume != nullptr && new_type.has_value() && !is_last_solid_part) {
|
||||
GUI_App &app = wxGetApp();
|
||||
Plater * plater = app.plater();
|
||||
Plater::TakeSnapshot snapshot(plater, _L("Change Text Type"), UndoRedo::SnapshotType::GizmoAction);
|
||||
m_volume->set_type(*new_type);
|
||||
|
||||
@ -612,8 +617,8 @@ void GLGizmoSVG::draw_model_type()
|
||||
// NOTE: on linux, function reorder_volumes_and_get_selection call GLCanvas3D::reload_scene(refresh_immediately = false)
|
||||
// which discard m_volume pointer and set it to nullptr also selection is cleared so gizmo is automaticaly closed
|
||||
auto &mng = m_parent.get_gizmos_manager();
|
||||
if (mng.get_current_type() != GLGizmosManager::Emboss)
|
||||
mng.open_gizmo(GLGizmosManager::Emboss);
|
||||
if (mng.get_current_type() != GLGizmosManager::Svg)
|
||||
mng.open_gizmo(GLGizmosManager::Svg);
|
||||
// TODO: select volume back - Ask @Sasa
|
||||
}
|
||||
}
|
||||
@ -623,17 +628,6 @@ void GLGizmoSVG::draw_model_type()
|
||||
// priv namespace implementation
|
||||
///////////////
|
||||
|
||||
bool priv::is_valid(ModelVolumeType volume_type)
|
||||
{
|
||||
if (volume_type == ModelVolumeType::MODEL_PART ||
|
||||
volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
|
||||
volume_type == ModelVolumeType::PARAMETER_MODIFIER )
|
||||
return true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << "Can't create embossed SVG with this type: " << (int) volume_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string priv::get_file_name(const std::string &file_path)
|
||||
{
|
||||
if (file_path.empty())
|
||||
@ -660,6 +654,14 @@ std::string priv::get_file_name(const std::string &file_path)
|
||||
return file_path.substr(offset, count);
|
||||
}
|
||||
|
||||
std::string priv::volume_name(const EmbossShape &shape)
|
||||
{
|
||||
std::string file_name = get_file_name(shape.svg_file_path);
|
||||
if (!file_name.empty())
|
||||
return file_name;
|
||||
return "SVG shape";
|
||||
}
|
||||
|
||||
std::string priv::choose_svg_file()
|
||||
{
|
||||
wxWindow* parent = nullptr;
|
||||
@ -689,5 +691,57 @@ std::string priv::choose_svg_file()
|
||||
return path;
|
||||
}
|
||||
|
||||
ExPolygons priv::default_shape() {
|
||||
std::string file = Slic3r::resources_dir() + "/icons/question.svg";
|
||||
NSVGimage *image = nsvgParseFromFile(file.c_str(), "px", 96.0f);
|
||||
ExPolygons shape = NSVGUtils::to_ExPolygons(image);
|
||||
nsvgDelete(image);
|
||||
return shape;
|
||||
}
|
||||
|
||||
EmbossShape priv::select_shape() {
|
||||
EmbossShape shape;
|
||||
shape.depth = 10.;
|
||||
shape.distance = 0;
|
||||
shape.use_surface = false;
|
||||
|
||||
shape.svg_file_path = choose_svg_file();
|
||||
if (shape.svg_file_path.empty())
|
||||
return {};
|
||||
|
||||
NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), "mm", 96.0f);
|
||||
ScopeGuard sg([image]() { nsvgDelete(image); });
|
||||
shape.shapes = NSVGUtils::to_ExPolygons(image);
|
||||
|
||||
// TODO: get scale from file
|
||||
shape.scale = 1.;
|
||||
|
||||
// Must contain some shapes !!!
|
||||
if (shape.shapes.empty())
|
||||
shape.shapes = default_shape();
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
DataBasePtr priv::create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel) {
|
||||
EmbossShape shape = priv::select_shape();
|
||||
|
||||
if (shape.shapes.empty())
|
||||
// canceled selection of SVG file
|
||||
return nullptr;
|
||||
|
||||
// Cancel previous Job, when it is in process
|
||||
// worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs
|
||||
// Cancel only EmbossUpdateJob no others
|
||||
if (cancel != nullptr)
|
||||
cancel->store(true);
|
||||
// create new shared ptr to cancel new job
|
||||
cancel = std::make_shared<std::atomic<bool>>(false);
|
||||
|
||||
std::string name = priv::volume_name(shape);
|
||||
|
||||
return std::make_unique<DataBase>(name, cancel /*copy*/, std::move(shape));
|
||||
}
|
||||
|
||||
// any existing icon filename to not influence GUI
|
||||
const std::string GLGizmoSVG::M_ICON_FILENAME = "cut.svg";
|
||||
|
@ -37,29 +37,29 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
/// <param name="mouse_pos">Define position of new volume</param>
|
||||
void create_volume(const std::string& svg_file_path, ModelVolumeType volume_type, const Vec2d &mouse_pos);
|
||||
void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
|
||||
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||
bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
|
||||
|
||||
/// <summary>
|
||||
/// Create new text without given position
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||
void create_volume(const std::string &svg_file_path, ModelVolumeType volume_type);
|
||||
void create_volume(ModelVolumeType volume_type); // first open file dialog
|
||||
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||
bool create_volume(ModelVolumeType volume_type); // first open file dialog
|
||||
|
||||
/// <summary>
|
||||
/// Check whether volume is object containing only emboss volume
|
||||
/// </summary>
|
||||
/// <param name="volume">Pointer to volume</param>
|
||||
/// <returns>True when object otherwise False</returns>
|
||||
static bool is_svg_object(const ModelVolume *volume);
|
||||
static bool is_svg_object(const ModelVolume &volume);
|
||||
|
||||
/// <summary>
|
||||
/// Check whether volume has emboss data
|
||||
/// </summary>
|
||||
/// <param name="volume">Pointer to volume</param>
|
||||
/// <returns>True when constain emboss data otherwise False</returns>
|
||||
static bool is_svg(const ModelVolume *volume);
|
||||
static bool is_svg(const ModelVolume &volume);
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <libslic3r/Model.hpp>
|
||||
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
|
||||
#include <libslic3r/CutSurface.hpp> // use surface cuts
|
||||
#include <libslic3r/BuildVolume.hpp> // create object
|
||||
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
@ -27,6 +28,120 @@ using namespace Slic3r::Emboss;
|
||||
using namespace Slic3r::GUI;
|
||||
using namespace Slic3r::GUI::Emboss;
|
||||
|
||||
// Private implementation for create volume and objects jobs
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
/// <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 DataCreateVolume
|
||||
{
|
||||
// Hold data about shape
|
||||
DataBasePtr base;
|
||||
|
||||
// define embossed volume type
|
||||
ModelVolumeType volume_type;
|
||||
|
||||
// parent ModelObject index where to create volume
|
||||
ObjectID object_id;
|
||||
|
||||
// new created volume transformation
|
||||
std::optional<Transform3d> trmat;
|
||||
|
||||
// Define which gizmo open on the success
|
||||
GLGizmosManager::EType gizmo;
|
||||
};
|
||||
|
||||
/// <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 CreateVolumeJob : public Job
|
||||
{
|
||||
DataCreateVolume m_input;
|
||||
TriangleMesh m_result;
|
||||
|
||||
public:
|
||||
CreateVolumeJob(DataCreateVolume &&input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to create ModelObject in job
|
||||
/// Object is placed on bed under screen coor
|
||||
/// OR to center of scene when it is out of bed shape
|
||||
/// </summary>
|
||||
struct DataCreateObject
|
||||
{
|
||||
// Hold data about shape
|
||||
DataBasePtr base;
|
||||
|
||||
// define position on screen where to create object
|
||||
Vec2d screen_coor;
|
||||
|
||||
// projection property
|
||||
Camera camera;
|
||||
|
||||
// shape of bed in case of create volume on bed
|
||||
std::vector<Vec2d> bed_shape;
|
||||
|
||||
// Define which gizmo open on the success
|
||||
GLGizmosManager::EType gizmo;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create new TextObject on the platter
|
||||
/// Should not be stopped
|
||||
/// </summary>
|
||||
class CreateObjectJob : public Job
|
||||
{
|
||||
DataCreateObject m_input;
|
||||
TriangleMesh m_result;
|
||||
Transform3d m_transformation;
|
||||
|
||||
public:
|
||||
CreateObjectJob(DataCreateObject &&input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to create(cut) volume from surface object in job
|
||||
/// </summary>
|
||||
struct CreateSurfaceVolumeData : public SurfaceVolumeData
|
||||
{
|
||||
// Hold data about shape
|
||||
DataBasePtr base;
|
||||
|
||||
// define embossed volume type
|
||||
ModelVolumeType volume_type;
|
||||
|
||||
// parent ModelObject index where to create volume
|
||||
ObjectID object_id;
|
||||
|
||||
// Define which gizmo open on the success
|
||||
GLGizmosManager::EType gizmo;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Cut surface from object and create cutted volume
|
||||
/// Should not be stopped
|
||||
/// </summary>
|
||||
class CreateSurfaceVolumeJob : public Job
|
||||
{
|
||||
CreateSurfaceVolumeData m_input;
|
||||
TriangleMesh m_result;
|
||||
|
||||
public:
|
||||
CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
} // namespace Slic3r::GUI::Emboss
|
||||
|
||||
// private namespace
|
||||
namespace priv{
|
||||
// create sure that emboss object is bigger than source object [in mm]
|
||||
@ -37,12 +152,13 @@ constexpr float safe_extension = 1.0f;
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
|
||||
bool check(const DataCreateVolume &input, bool is_main_thread = false);
|
||||
bool check(const DataCreateObject &input);
|
||||
bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
|
||||
bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||
bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||
static bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
|
||||
static bool check(GLGizmosManager::EType gizmo);
|
||||
static bool check(const DataCreateVolume &input, bool is_main_thread = false);
|
||||
static bool check(const DataCreateObject &input);
|
||||
static bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
|
||||
static bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||
static bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||
|
||||
// <summary>
|
||||
/// Try to create mesh from text
|
||||
@ -85,8 +201,9 @@ static void update_name_in_list(const ObjectList &object_list, const ModelVolume
|
||||
/// <param name="type">Type of new volume</param>
|
||||
/// <param name="trmat">Transformation of volume inside of object</param>
|
||||
/// <param name="data">Text configuration and New VolumeName</param>
|
||||
static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id,
|
||||
const ModelVolumeType type, const Transform3d trmat, const DataBase &data);
|
||||
/// <param name="gizmo">Gizmo to open</param>
|
||||
static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id, const ModelVolumeType type,
|
||||
const std::optional<Transform3d>& trmat, const DataBase &data, GLGizmosManager::EType gizmo);
|
||||
|
||||
/// <summary>
|
||||
/// Select Volume from objects
|
||||
@ -124,6 +241,14 @@ static OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Tr
|
||||
/// <returns>Extruded object from cuted surace</returns>
|
||||
static TriangleMesh cut_surface(/*const*/ DataBase &input1, const SurfaceVolumeData &input2, std::function<bool()> was_canceled);
|
||||
|
||||
/// <summary>
|
||||
/// Copied triangles from object to be able create mesh for cut surface from
|
||||
/// </summary>
|
||||
/// <param name="volumes">Source object volumes for cut surface from</param>
|
||||
/// <param name="text_volume_id">Source volume id</param>
|
||||
/// <returns>Source data for cut surface from</returns>
|
||||
static SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id = {});
|
||||
|
||||
static void create_message(const std::string &message); // only in finalize
|
||||
static bool process(std::exception_ptr &eptr);
|
||||
static bool finalize(bool canceled, std::exception_ptr &eptr, const DataBase &input);
|
||||
@ -141,6 +266,11 @@ auto was_canceled(Job::Ctl &ctl, DataBase &base){
|
||||
|
||||
}// namespace priv
|
||||
|
||||
void Slic3r::GUI::Emboss::DataBase::write(ModelVolume &volume) const{
|
||||
volume.name = volume_name;
|
||||
volume.emboss_shape = shape;
|
||||
}
|
||||
|
||||
/////////////////
|
||||
/// Create Volume
|
||||
CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input): m_input(std::move(input)){ assert(priv::check(m_input, true)); }
|
||||
@ -159,7 +289,7 @@ void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
return;
|
||||
if (m_result.its.empty())
|
||||
return priv::create_message(_u8L("Can't create empty volume."));
|
||||
priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, *m_input.base);
|
||||
priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, *m_input.base, m_input.gizmo);
|
||||
}
|
||||
|
||||
|
||||
@ -194,7 +324,7 @@ void CreateObjectJob::process(Ctl &ctl)
|
||||
bed_shape_.reserve(m_input.bed_shape.size());
|
||||
for (const Vec2d &p : m_input.bed_shape)
|
||||
bed_shape_.emplace_back(p.cast<int>());
|
||||
Polygon bed(bed_shape_);
|
||||
Slic3r::Polygon bed(bed_shape_);
|
||||
if (!bed.contains(bed_coor.cast<int>()))
|
||||
// mouse pose is out of build plate so create object in center of plate
|
||||
bed_coor = bed.centroid().cast<double>();
|
||||
@ -254,8 +384,8 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
// Manager::reset_all() So Gizmo could be closed before end of creation object
|
||||
GLCanvas3D *canvas = plater->canvas3D();
|
||||
GLGizmosManager &manager = canvas->get_gizmos_manager();
|
||||
if (manager.get_current_type() != GLGizmosManager::Emboss)
|
||||
manager.open_gizmo(GLGizmosManager::Emboss);
|
||||
if (manager.get_current_type() != m_input.gizmo)
|
||||
manager.open_gizmo(m_input.gizmo);
|
||||
|
||||
// redraw scene
|
||||
canvas->reload_scene(true);
|
||||
@ -306,7 +436,7 @@ void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
if (!priv::finalize(canceled, eptr, *m_input.base))
|
||||
return;
|
||||
priv::create_volume(std::move(m_result), m_input.object_id,
|
||||
m_input.volume_type, m_input.transform, *m_input.base);
|
||||
m_input.volume_type, m_input.transform, *m_input.base, m_input.gizmo);
|
||||
}
|
||||
|
||||
/////////////////
|
||||
@ -335,143 +465,203 @@ void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
priv::update_volume(std::move(m_result), m_input, tr);
|
||||
}
|
||||
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
namespace priv {
|
||||
/// <summary>
|
||||
/// Check if volume type is possible use for new text volume
|
||||
/// </summary>
|
||||
/// <param name="volume_type">Type</param>
|
||||
/// <returns>True when allowed otherwise false</returns>
|
||||
static bool is_valid(ModelVolumeType volume_type);
|
||||
|
||||
/// <summary>
|
||||
/// Start job for add new volume to object with given transformation
|
||||
/// </summary>
|
||||
/// <param name="worker">Define where to queue the job. e.g. wxGetApp().plater()->get_ui_job_worker()</param>
|
||||
/// <param name="object">Define where to add</param>
|
||||
/// <param name="volume_tr">Wanted volume transformation, when not set will be calculated after creation to be near the object</param>
|
||||
/// <param name="data">Define what to emboss - shape</param>
|
||||
/// <param name="volume_type">Type of volume: Part, negative, modifier</param>
|
||||
/// <param name="gizmo">Define which gizmo open on the success</param>
|
||||
/// <returns>Nullptr when job is sucessfully add to worker otherwise return data to be processed different way</returns>
|
||||
static bool start_create_volume_job(Worker &worker, const ModelObject &object, const std::optional<Transform3d>& volume_tr, DataBasePtr data, ModelVolumeType volume_type, GLGizmosManager::EType gizmo);
|
||||
|
||||
/// <summary>
|
||||
/// Find volume in selected objects with closest convex hull to screen center.
|
||||
/// </summary>
|
||||
/// <param name="selection">Define where to search for closest</param>
|
||||
/// <param name="screen_center">Canvas center(dependent on camera settings)</param>
|
||||
/// <param name="objects">Actual objects</param>
|
||||
/// <param name="closest_center">OUT: coordinate of controid of closest volume</param>
|
||||
/// <returns>closest volume when exists otherwise nullptr</returns>
|
||||
static const GLVolume *find_closest(
|
||||
const Selection &selection, const Vec2d &screen_center,
|
||||
const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center);
|
||||
|
||||
/// <summary>
|
||||
/// Start job for add object with text into scene
|
||||
/// </summary>
|
||||
/// <param name="plater">Contain worker and build shape</param>
|
||||
/// <param name="emboss_data">Define params of text</param>
|
||||
/// <param name="coor">Screen coordinat, where to create new object laying on bed</param>
|
||||
/// <param name="gizmo">Define which gizmo open on the success</param>
|
||||
/// <returns>True when can add job to worker otherwise FALSE</returns>
|
||||
static bool start_create_object_job(Plater &plater, DataBasePtr emboss_data, const Vec2d &coor, GLGizmosManager::EType gizmo);
|
||||
|
||||
/// <summary>
|
||||
/// Start job to create volume on the surface of object
|
||||
/// </summary>
|
||||
/// <param name="plater">scene RayCasters + Objects + Camera + worker</param>
|
||||
/// <param name="data">Describe what to emboss</param>
|
||||
/// <param name="volume_type">Type of new volume</param>
|
||||
/// <param name="screen_coor">Where to add</param>
|
||||
/// <param name="gl_volume">Surface point is belonge to</param>
|
||||
/// <param name="raycaster">For project on surface</param>
|
||||
/// <param name="gizmo">Define which gizmo open on the success</param>
|
||||
/// <param name="distance">Distance from surface</param>
|
||||
/// <param name="angle">Angle around emboss direction</param>
|
||||
/// <param name="success">True on add job to worker otherwise false</param>
|
||||
/// <returns>Nullptr when job is sucessfully add to worker otherwise return data to be processed different way</returns>
|
||||
static DataBasePtr start_create_volume_on_surface_job(Plater &plater,
|
||||
DataBasePtr data,
|
||||
ModelVolumeType volume_type,
|
||||
const Vec2d &screen_coor,
|
||||
const GLVolume &gl_volume,
|
||||
RaycastManager &raycaster,
|
||||
GLGizmosManager::EType gizmo,
|
||||
const std::optional<float> &distance,
|
||||
const std::optional<float> &angle,
|
||||
bool &success);
|
||||
|
||||
SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> 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)
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
|
||||
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;
|
||||
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);
|
||||
return priv::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 start_create_volume(Plater *plater_ptr,
|
||||
DataBasePtr data,
|
||||
ModelVolumeType volume_type,
|
||||
RaycastManager &raycaster,
|
||||
unsigned char gizmo,
|
||||
const Vec2d &mouse_pos,
|
||||
const std::optional<float> &distance,
|
||||
const std::optional<float> &angle)
|
||||
{
|
||||
bool &use_surface = data->shape.use_surface;
|
||||
std::unique_ptr<GUI::Job> 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<CreateSurfaceVolumeJob>(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<CreateVolumeJob>(std::move(create_volume_data));
|
||||
}
|
||||
return queue_job(worker, std::move(job));
|
||||
if (!priv::is_valid(volume_type))
|
||||
return false;
|
||||
|
||||
assert(plater_ptr);
|
||||
if (plater_ptr == nullptr)
|
||||
return false;
|
||||
Plater &plater = *plater_ptr;
|
||||
|
||||
GLCanvas3D *canvas_ptr = plater.get_current_canvas3D();
|
||||
assert(canvas_ptr);
|
||||
if (canvas_ptr == nullptr)
|
||||
return false;
|
||||
|
||||
GLGizmosManager::EType gizmo_type = static_cast<GLGizmosManager::EType>(gizmo);
|
||||
|
||||
GLVolume *gl_volume = get_first_hovered_gl_volume(*canvas_ptr);
|
||||
if (gl_volume == nullptr)
|
||||
// object is not under mouse position soo create object on plater
|
||||
return priv::start_create_object_job(plater, std::move(data), mouse_pos, gizmo_type);
|
||||
|
||||
bool success = true;
|
||||
DataBasePtr data2 = priv::start_create_volume_on_surface_job(plater, std::move(data),
|
||||
volume_type, mouse_pos, *gl_volume, raycaster, gizmo_type, distance, angle, success);
|
||||
|
||||
// Is successfull created job for add volume on surface?
|
||||
if (success)
|
||||
return true;
|
||||
|
||||
// not success and consume data
|
||||
if (data2 == nullptr)
|
||||
return false;
|
||||
|
||||
// Can't create on coordinate try to create somewhere
|
||||
return start_create_volume_without_position(plater_ptr, std::move(data2), volume_type, raycaster, gizmo, distance, angle);
|
||||
}
|
||||
|
||||
std::optional<Transform3d> 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<float> &distance,
|
||||
const std::optional<float> &angle)
|
||||
bool start_create_volume_without_position(Plater *plater_ptr,
|
||||
DataBasePtr data,
|
||||
ModelVolumeType volume_type,
|
||||
RaycastManager &raycaster,
|
||||
unsigned char gizmo,
|
||||
const std::optional<float> &distance,
|
||||
const std::optional<float> &angle)
|
||||
{
|
||||
auto cond = RaycastManager::AllowVolumes({volume.id().id});
|
||||
RaycastManager::Meshes meshes = create_meshes(canvas, cond);
|
||||
raycaster.actualize(instance, &cond, &meshes);
|
||||
if (!priv::is_valid(volume_type))
|
||||
return false;
|
||||
|
||||
std::optional<RaycastManager::Hit> hit = ray_from_camera(raycaster, screen_coor, camera, &cond);
|
||||
assert(plater_ptr);
|
||||
if (plater_ptr == nullptr)
|
||||
return false;
|
||||
Plater &plater = *plater_ptr;
|
||||
|
||||
// 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 {};
|
||||
GLCanvas3D *canvas_ptr = plater.get_current_canvas3D();
|
||||
assert(canvas_ptr);
|
||||
if (canvas_ptr == nullptr)
|
||||
return false;
|
||||
const GLCanvas3D &canvas = *canvas_ptr;
|
||||
|
||||
// Create result volume transformation
|
||||
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, Slic3r::GUI::up_limit);
|
||||
// select position by camera position and view direction
|
||||
const Selection &selection = canvas.get_selection();
|
||||
int object_idx = selection.get_object_idx();
|
||||
|
||||
apply_transformation(angle, distance, surface_trmat);
|
||||
Size s = canvas.get_canvas_size();
|
||||
Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
|
||||
const ModelObjectPtrs &objects = selection.get_model()->objects;
|
||||
|
||||
return instance.get_matrix().inverse() * surface_trmat;
|
||||
}
|
||||
GLGizmosManager::EType gizmo_type = static_cast<GLGizmosManager::EType>(gizmo);
|
||||
|
||||
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);
|
||||
}
|
||||
// No selected object so create new object
|
||||
if (selection.is_empty() || object_idx < 0 ||
|
||||
static_cast<size_t>(object_idx) >= objects.size())
|
||||
// create Object on center of screen
|
||||
// when ray throw center of screen not hit bed it create object on center of bed
|
||||
return priv::start_create_object_job(plater, std::move(data), screen_center, gizmo_type);
|
||||
|
||||
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;
|
||||
// create volume inside of selected object
|
||||
Vec2d coor;
|
||||
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||
const GLVolume *gl_volume = priv::find_closest(selection, screen_center, camera, objects, &coor);
|
||||
|
||||
double center_sq_distance = std::numeric_limits<double>::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<double>();
|
||||
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;
|
||||
if (gl_volume == nullptr)
|
||||
return priv::start_create_object_job(plater, std::move(data), screen_center, gizmo_type);
|
||||
|
||||
bool success = true;
|
||||
DataBasePtr data2 = priv::start_create_volume_on_surface_job(plater, std::move(data),
|
||||
volume_type, coor, *gl_volume, raycaster, gizmo_type, distance, angle, success);
|
||||
|
||||
double distance = d.squaredNorm();
|
||||
if (center_sq_distance < distance)
|
||||
continue;
|
||||
center_sq_distance = distance;
|
||||
// Is successfull created job for add volume on surface?
|
||||
if (success)
|
||||
return true;
|
||||
|
||||
*closest_center = c;
|
||||
closest = gl_volume;
|
||||
}
|
||||
return closest;
|
||||
// not success and consume data
|
||||
if (data2 == nullptr)
|
||||
return false;
|
||||
|
||||
// In centroid of convex hull is not hit with object. e.g. torid
|
||||
// soo create transfomation on border of object
|
||||
|
||||
// there is no point on surface so no use of surface will be applied
|
||||
if (data2->shape.use_surface)
|
||||
data2->shape.use_surface = false;
|
||||
|
||||
Worker &worker = plater.get_ui_job_worker();
|
||||
const ModelObject *object = get_model_object(*gl_volume, objects);
|
||||
if (object == nullptr)
|
||||
return false;
|
||||
|
||||
return priv::start_create_volume_job(worker, *object, {}, std::move(data2), volume_type, gizmo_type);
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI::Emboss
|
||||
@ -495,6 +685,13 @@ bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface)
|
||||
//res &= input.text_configuration.style.prop.use_surface == use_surface;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool priv::check(GLGizmosManager::EType gizmo)
|
||||
{
|
||||
assert(gizmo == GLGizmosManager::Emboss || gizmo == GLGizmosManager::Svg);
|
||||
return gizmo == GLGizmosManager::Emboss || gizmo == GLGizmosManager::Svg;
|
||||
}
|
||||
|
||||
bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
|
||||
bool check_fontfile = false;
|
||||
assert(input.base != nullptr);
|
||||
@ -504,6 +701,7 @@ bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
|
||||
res &= input.volume_type != ModelVolumeType::INVALID;
|
||||
assert(input.object_id.id >= 0);
|
||||
res &= input.object_id.id >= 0;
|
||||
res &= check(input.gizmo);
|
||||
return res;
|
||||
}
|
||||
bool priv::check(const DataCreateObject &input) {
|
||||
@ -517,6 +715,7 @@ bool priv::check(const DataCreateObject &input) {
|
||||
res &= input.screen_coor.y() >= 0.;
|
||||
assert(input.bed_shape.size() >= 3); // at least triangle
|
||||
res &= input.bed_shape.size() >= 3;
|
||||
res &= check(input.gizmo);
|
||||
return res;
|
||||
}
|
||||
bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){
|
||||
@ -542,6 +741,7 @@ bool priv::check(const CreateSurfaceVolumeData &input, bool is_main_thread)
|
||||
res &= check(*input.base, is_main_thread, use_surface);
|
||||
assert(!input.sources.empty());
|
||||
res &= !input.sources.empty();
|
||||
res &= check(input.gizmo);
|
||||
return res;
|
||||
}
|
||||
bool priv::check(const UpdateSurfaceVolumeData &input, bool is_main_thread){
|
||||
@ -711,9 +911,12 @@ void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3
|
||||
UpdateJob::update_volume(volume, std::move(mesh), *data.base);
|
||||
}
|
||||
|
||||
void priv::create_volume(
|
||||
TriangleMesh &&mesh, const ObjectID& object_id,
|
||||
const ModelVolumeType type, const Transform3d trmat, const DataBase &data)
|
||||
void priv::create_volume(TriangleMesh &&mesh,
|
||||
const ObjectID &object_id,
|
||||
const ModelVolumeType type,
|
||||
const std::optional<Transform3d> &trmat,
|
||||
const DataBase &data,
|
||||
GLGizmosManager::EType gizmo)
|
||||
{
|
||||
GUI_App &app = wxGetApp();
|
||||
Plater *plater = app.plater();
|
||||
@ -741,6 +944,13 @@ void priv::create_volume(
|
||||
|
||||
plater->take_snapshot(_L("Add Emboss text Volume"));
|
||||
|
||||
BoundingBoxf3 instance_bb;
|
||||
if (!trmat.has_value()) {
|
||||
// used for align to instance
|
||||
size_t instance_index = 0; // must exist
|
||||
instance_bb = obj->instance_bounding_box(instance_index);
|
||||
}
|
||||
|
||||
// NOTE: be carefull add volume also center mesh !!!
|
||||
// So first add simple shape(convex hull is also calculated)
|
||||
ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type);
|
||||
@ -757,7 +967,24 @@ void priv::create_volume(
|
||||
volume->source.is_from_builtin_objects = true;
|
||||
|
||||
volume->name = data.volume_name; // copy
|
||||
volume->set_transformation(trmat);
|
||||
|
||||
if (trmat.has_value()) {
|
||||
volume->set_transformation(*trmat);
|
||||
} else {
|
||||
assert(!data.shape.use_surface);
|
||||
// Create transformation for volume near from object(defined by glVolume)
|
||||
// Transformation is inspired add generic volumes in ObjectList::load_generic_subobject
|
||||
Vec3d volume_size = volume->mesh().bounding_box().size();
|
||||
// Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
|
||||
Vec3d offset_tr(0, // center of instance - Can't suggest width of text before it will be created
|
||||
-instance_bb.size().y() / 2 - volume_size.y() / 2, // under
|
||||
volume_size.z() / 2 - instance_bb.size().z() / 2); // lay on bed
|
||||
// use same instance as for calculation of instance_bounding_box
|
||||
Transform3d tr = obj->instances.front()->get_transformation().get_matrix_no_offset().inverse();
|
||||
Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr);
|
||||
volume->set_transformation(volume_trmat);
|
||||
}
|
||||
|
||||
data.write(*volume);
|
||||
|
||||
// update printable state on canvas
|
||||
@ -935,6 +1162,26 @@ TriangleMesh priv::cut_surface(DataBase& base, const SurfaceVolumeData& input2,
|
||||
return TriangleMesh(std::move(new_its));
|
||||
}
|
||||
|
||||
SurfaceVolumeData::ModelSources priv::create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> 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;
|
||||
}
|
||||
|
||||
bool priv::process(std::exception_ptr &eptr) {
|
||||
if (!eptr) return false;
|
||||
try {
|
||||
@ -956,6 +1203,141 @@ bool priv::finalize(bool canceled, std::exception_ptr &eptr, const DataBase &inp
|
||||
return !process(eptr);
|
||||
}
|
||||
|
||||
bool priv::is_valid(ModelVolumeType volume_type)
|
||||
{
|
||||
if (volume_type == ModelVolumeType::MODEL_PART ||
|
||||
volume_type == ModelVolumeType::NEGATIVE_VOLUME ||
|
||||
volume_type == ModelVolumeType::PARAMETER_MODIFIER )
|
||||
return true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int) volume_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool priv::start_create_volume_job(
|
||||
Worker &worker, const ModelObject &object, const std::optional<Transform3d>& volume_tr, DataBasePtr data, ModelVolumeType volume_type, GLGizmosManager::EType gizmo)
|
||||
{
|
||||
bool &use_surface = data->shape.use_surface;
|
||||
std::unique_ptr<GUI::Job> job;
|
||||
if (use_surface) {
|
||||
// Model to cut surface from.
|
||||
SurfaceVolumeData::ModelSources sources = create_sources(object.volumes);
|
||||
if (sources.empty() || !volume_tr.has_value()) {
|
||||
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(), gizmo};
|
||||
job = std::make_unique<CreateSurfaceVolumeJob>(std::move(surface_data));
|
||||
}
|
||||
}
|
||||
if (!use_surface) {
|
||||
// create volume
|
||||
DataCreateVolume create_volume_data{std::move(data), volume_type, object.id(), volume_tr, gizmo};
|
||||
job = std::make_unique<CreateVolumeJob>(std::move(create_volume_data));
|
||||
}
|
||||
return queue_job(worker, std::move(job));
|
||||
}
|
||||
|
||||
const GLVolume * priv::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<double>::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<double>();
|
||||
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;
|
||||
}
|
||||
|
||||
bool priv::start_create_object_job(Plater &plater, DataBasePtr emboss_data, const Vec2d &coor, GLGizmosManager::EType gizmo)
|
||||
{
|
||||
const Camera &camera = plater.get_camera();
|
||||
const Pointfs &bed_shape = plater.build_volume().bed_shape();
|
||||
|
||||
DataCreateObject data{std::move(emboss_data), coor, camera, bed_shape, gizmo};
|
||||
auto job = std::make_unique<CreateObjectJob>(std::move(data));
|
||||
|
||||
Worker &worker = plater.get_ui_job_worker();
|
||||
return queue_job(worker, std::move(job));
|
||||
}
|
||||
|
||||
DataBasePtr priv::start_create_volume_on_surface_job(Plater &plater,
|
||||
DataBasePtr data,
|
||||
ModelVolumeType volume_type,
|
||||
const Vec2d &screen_coor,
|
||||
const GLVolume &gl_volume,
|
||||
RaycastManager &raycaster,
|
||||
GLGizmosManager::EType gizmo,
|
||||
const std::optional<float> &distance,
|
||||
const std::optional<float> &angle,
|
||||
bool &success)
|
||||
{
|
||||
success = false;
|
||||
const ModelObjectPtrs &objects = plater.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 data;
|
||||
}
|
||||
|
||||
const Camera &camera = plater.get_camera();
|
||||
GLCanvas3D &canvas = *plater.get_current_canvas3D();
|
||||
|
||||
auto cond = RaycastManager::AllowVolumes({volume->id().id});
|
||||
RaycastManager::Meshes meshes = create_meshes(canvas, cond);
|
||||
raycaster.actualize(*instance, &cond, &meshes);
|
||||
std::optional<RaycastManager::Hit> 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())
|
||||
// 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 data;
|
||||
|
||||
// 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);
|
||||
|
||||
Transform3d transform = instance->get_matrix().inverse() * surface_trmat;
|
||||
|
||||
// Try to cast ray into scene and find object for add volume
|
||||
Worker &worker = plater.get_ui_job_worker();
|
||||
|
||||
success = priv::start_create_volume_job(worker, *volume->get_object(), transform, std::move(data), volume_type, gizmo);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
|
@ -7,8 +7,7 @@
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
#include "libslic3r/EmbossShape.hpp"
|
||||
#include "libslic3r/Point.hpp" // Transform3d
|
||||
|
||||
#include "slic3r/Utils/RaycastManager.hpp"
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
|
||||
#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
@ -17,16 +16,12 @@
|
||||
|
||||
// forward declarations
|
||||
namespace Slic3r {
|
||||
class GLVolume;
|
||||
class ModelVolume;
|
||||
class ModelObject;
|
||||
class TriangleMesh;
|
||||
typedef std::vector<ModelObject *> ModelObjectPtrs;
|
||||
typedef std::vector<ModelVolume *> ModelVolumePtrs;
|
||||
class ModelVolume;
|
||||
enum class ModelVolumeType : int;
|
||||
namespace GUI {
|
||||
class Selection;
|
||||
class RaycastManager;
|
||||
class Worker;
|
||||
class Plater;
|
||||
}}
|
||||
|
||||
namespace Slic3r::GUI::Emboss {
|
||||
@ -38,7 +33,7 @@ class DataBase
|
||||
{
|
||||
public:
|
||||
DataBase(std::string volume_name, std::shared_ptr<std::atomic<bool>> cancel) : volume_name(volume_name), cancel(std::move(cancel)) {}
|
||||
DataBase(std::string volume_name, std::shared_ptr<std::atomic<bool>> cancel, EmbossShape shape)
|
||||
DataBase(std::string volume_name, std::shared_ptr<std::atomic<bool>> cancel, EmbossShape&& shape)
|
||||
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape))
|
||||
{}
|
||||
virtual ~DataBase() {}
|
||||
@ -54,11 +49,7 @@ public:
|
||||
/// Write data how to reconstruct shape to volume
|
||||
/// </summary>
|
||||
/// <param name="volume">Data object for store emboss params</param>
|
||||
virtual void write(ModelVolume &volume) const
|
||||
{
|
||||
volume.name = volume_name;
|
||||
volume.emboss_shape = shape;
|
||||
};
|
||||
virtual void write(ModelVolume &volume) const;
|
||||
|
||||
// new volume name
|
||||
std::string volume_name;
|
||||
@ -70,77 +61,7 @@ public:
|
||||
// shape to emboss
|
||||
EmbossShape shape;
|
||||
};
|
||||
|
||||
/// <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 DataCreateVolume
|
||||
{
|
||||
// Hold data about shape
|
||||
std::unique_ptr<DataBase> base;
|
||||
|
||||
// define embossed volume type
|
||||
ModelVolumeType volume_type;
|
||||
|
||||
// parent ModelObject index where to create volume
|
||||
ObjectID object_id;
|
||||
|
||||
// new created volume transformation
|
||||
Transform3d trmat;
|
||||
};
|
||||
|
||||
/// <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 CreateVolumeJob : public Job
|
||||
{
|
||||
DataCreateVolume m_input;
|
||||
TriangleMesh m_result;
|
||||
|
||||
public:
|
||||
CreateVolumeJob(DataCreateVolume&& input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to create ModelObject in job
|
||||
/// Object is placed on bed under screen coor
|
||||
/// OR to center of scene when it is out of bed shape
|
||||
/// </summary>
|
||||
struct DataCreateObject
|
||||
{
|
||||
// Hold data about shape
|
||||
std::unique_ptr<DataBase> base;
|
||||
|
||||
// define position on screen where to create object
|
||||
Vec2d screen_coor;
|
||||
|
||||
// projection property
|
||||
Camera camera;
|
||||
|
||||
// shape of bed in case of create volume on bed
|
||||
std::vector<Vec2d> bed_shape;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create new TextObject on the platter
|
||||
/// Should not be stopped
|
||||
/// </summary>
|
||||
class CreateObjectJob : public Job
|
||||
{
|
||||
DataCreateObject m_input;
|
||||
TriangleMesh m_result;
|
||||
Transform3d m_transformation;
|
||||
public:
|
||||
CreateObjectJob(DataCreateObject&& input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
using DataBasePtr = std::unique_ptr<DataBase>;
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to update embossed text object in job
|
||||
@ -148,7 +69,7 @@ public:
|
||||
struct DataUpdate
|
||||
{
|
||||
// Hold data about shape
|
||||
std::unique_ptr<DataBase> base;
|
||||
DataBasePtr base;
|
||||
|
||||
// unique identifier of volume to change
|
||||
ObjectID volume_id;
|
||||
@ -212,35 +133,6 @@ struct SurfaceVolumeData
|
||||
ModelSources sources;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to create(cut) volume from surface object in job
|
||||
/// </summary>
|
||||
struct CreateSurfaceVolumeData : public SurfaceVolumeData{
|
||||
// Hold data about shape
|
||||
std::unique_ptr<DataBase> base;
|
||||
|
||||
// define embossed volume type
|
||||
ModelVolumeType volume_type;
|
||||
|
||||
// parent ModelObject index where to create volume
|
||||
ObjectID object_id;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Cut surface from object and create cutted volume
|
||||
/// Should not be stopped
|
||||
/// </summary>
|
||||
class CreateSurfaceVolumeJob : public Job
|
||||
{
|
||||
CreateSurfaceVolumeData m_input;
|
||||
TriangleMesh m_result;
|
||||
|
||||
public:
|
||||
CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input);
|
||||
void process(Ctl &ctl) override;
|
||||
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hold neccessary data to update embossed text object in job
|
||||
/// </summary>
|
||||
@ -264,72 +156,43 @@ public:
|
||||
/// <summary>
|
||||
/// Copied triangles from object to be able create mesh for cut surface from
|
||||
/// </summary>
|
||||
/// <param name="volumes">Source object volumes for cut surface from</param>
|
||||
/// <param name="text_volume_id">Source volume id</param>
|
||||
/// <param name="volume">Define embossed volume</param>
|
||||
/// <returns>Source data for cut surface from</returns>
|
||||
SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id = {});
|
||||
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume &volume);
|
||||
|
||||
/// <summary>
|
||||
/// Copied triangles from object to be able create mesh for cut surface from
|
||||
/// Create new volume on position of mouse cursor
|
||||
/// </summary>
|
||||
/// <param name="text_volume">Define text in object</param>
|
||||
/// <returns>Source data for cut surface from</returns>
|
||||
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume);
|
||||
/// <param name="plater_ptr">canvas + camera + bed shape + </param>
|
||||
/// <param name="data">Shape of emboss</param>
|
||||
/// <param name="volume_type">New created volume type</param>
|
||||
/// <param name="raycaster">Knows object in scene</param>
|
||||
/// <param name="gizmo">Define which gizmo open on the success - enum GLGizmosManager::EType</param>
|
||||
/// <param name="mouse_pos">Define position where to create volume</param>
|
||||
/// <param name="distance">Wanted additionl move in Z(emboss) direction of new created volume</param>
|
||||
/// <param name="angle">Wanted additionl rotation around Z of new created volume</param>
|
||||
/// <returns>True on success otherwise False</returns>
|
||||
bool start_create_volume(Plater *plater_ptr,
|
||||
DataBasePtr data,
|
||||
ModelVolumeType volume_type,
|
||||
RaycastManager &raycaster,
|
||||
unsigned char gizmo,
|
||||
const Vec2d &mouse_pos,
|
||||
const std::optional<float> &distance = {},
|
||||
const std::optional<float> &angle = {});
|
||||
|
||||
using DataBasePtr = std::unique_ptr<DataBase>;
|
||||
|
||||
/// <summary>
|
||||
/// Start job for add new volume to object with given transformation
|
||||
/// Same as previous function but without mouse position
|
||||
/// Need to suggest position or put near the selection
|
||||
/// </summary>
|
||||
/// <param name="worker">Define where to queue the job. e.g. wxGetApp().plater()->get_ui_job_worker()</param>
|
||||
/// <param name="object">Define where to add</param>
|
||||
/// <param name="volume_tr">Wanted volume transformation</param>
|
||||
/// <param name="data">Define what to emboss - shape</param>
|
||||
/// <param name="volume_type">Type of volume: Part, negative, modifier</param>
|
||||
/// <return>True on success otherwise false</return>
|
||||
bool start_create_volume_job(Worker &worker, const ModelObject &object, const Transform3d volume_tr, DataBasePtr data, ModelVolumeType volume_type);
|
||||
|
||||
/// <summary>
|
||||
/// Start job for add new volume on surface of object defined by screen coor
|
||||
/// </summary>
|
||||
/// <param name="screen_coor">Mouse position which define position</param>
|
||||
/// <param name="volume">Volume to find surface for create</param>
|
||||
/// <param name="instance">Instance to find surface for create</param>
|
||||
/// <param name="raycaster">Ability to ray cast to model</param>
|
||||
/// <param name="canvas">Contain already used scene RayCasters</param>
|
||||
/// <param name="angle">Initial z move</param>
|
||||
/// <param name="angle">Initial z rotation</param>
|
||||
/// <returns>Volume transformation otherwise there is no hit surface by screen coor</returns>
|
||||
std::optional<Transform3d> 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<float> &distance = {},
|
||||
const std::optional<float> &angle = {});
|
||||
|
||||
/// <summary>
|
||||
/// Create transformation for volume near from object(defined by glVolume)
|
||||
/// </summary>
|
||||
/// <param name="gl_volume">Define object</param>
|
||||
/// <param name="objects">All objects</param>
|
||||
/// <param name="volume_height">Y Size of embossed volume [mm in instance]</param>
|
||||
/// <param name="volume_depth">Z size of embossed volume - emboss depth[mm in instance]</param>
|
||||
/// <returns>Transformation for new created volume</returns>
|
||||
Transform3d create_volume_transformation(const GLVolume& gl_volume, const ModelObjectPtrs &objects, float volume_height, float volume_depth);
|
||||
|
||||
/// <summary>
|
||||
/// Find volume in selected objects with closest convex hull to screen center.
|
||||
/// </summary>
|
||||
/// <param name="selection">Define where to search for closest</param>
|
||||
/// <param name="screen_center">Canvas center(dependent on camera settings)</param>
|
||||
/// <param name="objects">Actual objects</param>
|
||||
/// <param name="closest_center">OUT: coordinate of controid of closest volume</param>
|
||||
/// <returns>closest volume when exists otherwise nullptr</returns>
|
||||
const GLVolume *find_closest(
|
||||
const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center);
|
||||
|
||||
bool start_create_volume_without_position(Plater *plater_ptr,
|
||||
DataBasePtr data,
|
||||
ModelVolumeType volume_type,
|
||||
RaycastManager &raycaster,
|
||||
unsigned char gizmo,
|
||||
const std::optional<float> &distance = {},
|
||||
const std::optional<float> &angle = {});
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif // slic3r_EmbossJob_hpp_
|
||||
|
Loading…
x
Reference in New Issue
Block a user