mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-12 04:31:48 +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/GUI/Jobs/NotificationProgressIndicator.hpp"
|
||||||
#include "slic3r/Utils/WxFontUtils.hpp"
|
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||||
#include "slic3r/Utils/UndoRedo.hpp"
|
#include "slic3r/Utils/UndoRedo.hpp"
|
||||||
#include "slic3r/Utils/EmbossGui.hpp"
|
|
||||||
|
|
||||||
// TODO: remove include
|
// TODO: remove include
|
||||||
#include "libslic3r/SVG.hpp" // debug store
|
#include "libslic3r/SVG.hpp" // debug store
|
||||||
@ -108,14 +107,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
|
|||||||
|
|
||||||
// Private namespace with helper function for create volume
|
// Private namespace with helper function for create volume
|
||||||
namespace priv {
|
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>
|
/// <summary>
|
||||||
/// Prepare data for emboss
|
/// Prepare data for emboss
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -143,31 +134,6 @@ struct TextDataBase : public DataBase
|
|||||||
TextConfiguration text_configuration;
|
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
|
// Loaded icons enum
|
||||||
// Have to match order of files in function GLGizmoEmboss::init_icons()
|
// Have to match order of files in function GLGizmoEmboss::init_icons()
|
||||||
enum class IconType : unsigned {
|
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);
|
static bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false);
|
||||||
} // namespace priv
|
} // 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();
|
m_style_manager.discard_style_changes();
|
||||||
set_default_text();
|
set_default_text();
|
||||||
|
DataBasePtr base = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
||||||
std::unique_ptr<DataBase> emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
|
Plater *plater_ptr = wxGetApp().plater();
|
||||||
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);
|
|
||||||
|
|
||||||
const FontProp &fp = m_style_manager.get_style().prop;
|
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 start_create_volume(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Emboss, mouse_pos, fp.distance, fp.angle);
|
||||||
return create_volume(volume_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Designed for create volume without information of mouse in scene
|
// 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();
|
m_style_manager.discard_style_changes();
|
||||||
set_default_text();
|
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);
|
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;
|
Plater *plater_ptr = wxGetApp().plater();
|
||||||
// No selected object so create new object
|
const FontProp &fp = m_style_manager.get_style().prop;
|
||||||
if (selection.is_empty() || object_idx < 0 || static_cast<size_t>(object_idx) >= objects.size()) {
|
return start_create_volume_without_position(plater_ptr, std::move(emboss_data),
|
||||||
// create Object on center of screen
|
volume_type, m_raycast_manager, GLGizmosManager::Emboss, fp.distance, fp.angle);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||||
@ -1057,7 +980,7 @@ bool GLGizmoEmboss::process()
|
|||||||
|
|
||||||
if (use_surface) {
|
if (use_surface) {
|
||||||
// Model to cut surface from.
|
// 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;
|
if (sources.empty()) return false;
|
||||||
|
|
||||||
Transform3d text_tr = m_volume->get_matrix();
|
Transform3d text_tr = m_volume->get_matrix();
|
||||||
@ -3254,17 +3177,6 @@ bool GLGizmoEmboss::is_text_object(const ModelVolume *text) {
|
|||||||
// priv namespace implementation
|
// 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)
|
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))
|
: 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));
|
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)
|
ImVec2 priv::calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size)
|
||||||
{
|
{
|
||||||
const Selection::IndicesList indices = selection.get_volume_idxs();
|
const Selection::IndicesList indices = selection.get_volume_idxs();
|
||||||
|
@ -39,13 +39,13 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||||
/// <param name="mouse_pos">Define position of new volume</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>
|
/// <summary>
|
||||||
/// Create new text without given position
|
/// Create new text without given position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||||
void create_volume(ModelVolumeType volume_type);
|
bool create_volume(ModelVolumeType volume_type);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool on_init() override;
|
bool on_init() override;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/format.hpp"
|
#include "slic3r/GUI/format.hpp"
|
||||||
#include "slic3r/GUI/CameraUtils.hpp"
|
#include "slic3r/GUI/CameraUtils.hpp"
|
||||||
|
#include "slic3r/GUI/Jobs/EmbossJob.hpp"
|
||||||
#include "slic3r/Utils/UndoRedo.hpp"
|
#include "slic3r/Utils/UndoRedo.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Point.hpp"
|
#include "libslic3r/Point.hpp"
|
||||||
@ -32,11 +33,11 @@
|
|||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::Emboss;
|
using namespace Slic3r::Emboss;
|
||||||
using namespace Slic3r::GUI;
|
using namespace Slic3r::GUI;
|
||||||
|
using namespace Slic3r::GUI::Emboss;
|
||||||
|
|
||||||
namespace priv {
|
namespace priv {
|
||||||
// Variable keep limits for variables
|
// Variable keep limits for variables
|
||||||
static const struct Limits
|
static const struct Limits{
|
||||||
{
|
|
||||||
MinMax<float> emboss{0.01f, 1e4f}; // in mm
|
MinMax<float> emboss{0.01f, 1e4f}; // in mm
|
||||||
// distance text object from surface
|
// distance text object from surface
|
||||||
MinMax<float> angle{-180.f, 180.f}; // in degrees
|
MinMax<float> angle{-180.f, 180.f}; // in degrees
|
||||||
@ -55,20 +56,34 @@ GLGizmoSVG::GLGizmoSVG(GLCanvas3D &parent)
|
|||||||
|
|
||||||
// Private functions to create emboss volume
|
// Private functions to create emboss volume
|
||||||
namespace priv {
|
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>
|
/// <summary>
|
||||||
/// Open file dialog with svg files
|
/// Open file dialog with svg files
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>File path to svg</returns>
|
/// <returns>File path to svg</returns>
|
||||||
static std::string choose_svg_file();
|
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>
|
/// <summary>
|
||||||
/// Separate file name from file path.
|
/// Separate file name from file path.
|
||||||
/// String after last delimiter and before last point
|
/// 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>
|
/// <returns>File name without directory path</returns>
|
||||||
static std::string get_file_name(const std::string &file_path);
|
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
|
} // namespace priv
|
||||||
|
|
||||||
|
|
||||||
void GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos){
|
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
|
||||||
std::string path = priv::choose_svg_file();
|
{
|
||||||
if (path.empty()) return;
|
DataBasePtr base = priv::create_emboss_data_base(m_job_cancel);
|
||||||
create_volume(path, volume_type, mouse_pos);
|
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(ModelVolumeType volume_type) {
|
|
||||||
std::string path = priv::choose_svg_file();
|
|
||||||
if (path.empty()) return;
|
|
||||||
create_volume(path, volume_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoSVG::create_volume(const std::string &svg_file_path, ModelVolumeType volume_type, const Vec2d &mouse_pos) {
|
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type)
|
||||||
std::string name = priv::get_file_name(svg_file_path);
|
{
|
||||||
NSVGimage *image = nsvgParseFromFile(svg_file_path.c_str(), "mm", 96.0f);
|
DataBasePtr base = priv::create_emboss_data_base(m_job_cancel);
|
||||||
ExPolygons polys = NSVGUtils::to_ExPolygons(image);
|
Plater *plater_ptr = wxGetApp().plater();
|
||||||
nsvgDelete(image);
|
return start_create_volume_without_position(plater_ptr, std::move(base), volume_type, m_raycast_manager, GLGizmosManager::Svg);
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
bool GLGizmoSVG::is_svg_object(const ModelVolume &volume) {
|
||||||
if (volume == nullptr)
|
if (!volume.emboss_shape.has_value()) return false;
|
||||||
return false;
|
if (volume.type() != ModelVolumeType::MODEL_PART) return false;
|
||||||
return volume->emboss_shape.has_value();
|
for (const ModelVolume *v : volume.get_object()->volumes) {
|
||||||
}
|
if (v->id() == volume.id()) continue;
|
||||||
|
|
||||||
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;
|
|
||||||
if (v->type() == ModelVolumeType::MODEL_PART) return false;
|
if (v->type() == ModelVolumeType::MODEL_PART) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -423,7 +424,7 @@ void GLGizmoSVG::set_volume_by_selection()
|
|||||||
ImGuiWrapper::left_inputs();
|
ImGuiWrapper::left_inputs();
|
||||||
|
|
||||||
// is valid svg volume?
|
// is valid svg volume?
|
||||||
if (!is_svg(volume))
|
if (!is_svg(*volume))
|
||||||
return reset_volume();
|
return reset_volume();
|
||||||
|
|
||||||
// cancel previous job
|
// cancel previous job
|
||||||
@ -527,8 +528,8 @@ void GLGizmoSVG::close()
|
|||||||
{
|
{
|
||||||
// close gizmo == open it again
|
// close gizmo == open it again
|
||||||
auto& mng = m_parent.get_gizmos_manager();
|
auto& mng = m_parent.get_gizmos_manager();
|
||||||
if (mng.get_current_type() == GLGizmosManager::Emboss)
|
if (mng.get_current_type() == GLGizmosManager::Svg)
|
||||||
mng.open_gizmo(GLGizmosManager::Emboss);
|
mng.open_gizmo(GLGizmosManager::Svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoSVG::draw_window()
|
void GLGizmoSVG::draw_window()
|
||||||
@ -543,7 +544,8 @@ void GLGizmoSVG::draw_window()
|
|||||||
|
|
||||||
void GLGizmoSVG::draw_model_type()
|
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");
|
std::string title = _u8L("SVG is to object");
|
||||||
if (is_last_solid_part) {
|
if (is_last_solid_part) {
|
||||||
ImVec4 color{.5f, .5f, .5f, 1.f};
|
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))
|
if (ImGui::RadioButton(_u8L("Added").c_str(), type == part))
|
||||||
new_type = part;
|
new_type = part;
|
||||||
else if (ImGui::IsItemHovered())
|
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();
|
ImGui::SameLine();
|
||||||
|
|
||||||
std::string last_solid_part_hint = _u8L("You can't change a type of the last solid part of the object.");
|
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)
|
if (is_last_solid_part)
|
||||||
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
|
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
|
||||||
else if (type != negative)
|
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
|
GUI_App &app = wxGetApp();
|
||||||
if (wxGetApp().plater()->printer_technology() != ptSLA && wxGetApp().get_mode() != ConfigOptionMode::comSimple) {
|
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();
|
ImGui::SameLine();
|
||||||
if (ImGui::RadioButton(_u8L("Modifier").c_str(), type == modifier))
|
if (ImGui::RadioButton(_u8L("Modifier").c_str(), type == modifier))
|
||||||
new_type = modifier;
|
new_type = modifier;
|
||||||
@ -583,13 +590,11 @@ void GLGizmoSVG::draw_model_type()
|
|||||||
if (is_last_solid_part)
|
if (is_last_solid_part)
|
||||||
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
|
ImGui::SetTooltip("%s", last_solid_part_hint.c_str());
|
||||||
else if (type != modifier)
|
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) {
|
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);
|
Plater::TakeSnapshot snapshot(plater, _L("Change Text Type"), UndoRedo::SnapshotType::GizmoAction);
|
||||||
m_volume->set_type(*new_type);
|
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)
|
// 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
|
// 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();
|
auto &mng = m_parent.get_gizmos_manager();
|
||||||
if (mng.get_current_type() != GLGizmosManager::Emboss)
|
if (mng.get_current_type() != GLGizmosManager::Svg)
|
||||||
mng.open_gizmo(GLGizmosManager::Emboss);
|
mng.open_gizmo(GLGizmosManager::Svg);
|
||||||
// TODO: select volume back - Ask @Sasa
|
// TODO: select volume back - Ask @Sasa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,17 +628,6 @@ void GLGizmoSVG::draw_model_type()
|
|||||||
// priv namespace implementation
|
// 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)
|
std::string priv::get_file_name(const std::string &file_path)
|
||||||
{
|
{
|
||||||
if (file_path.empty())
|
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);
|
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()
|
std::string priv::choose_svg_file()
|
||||||
{
|
{
|
||||||
wxWindow* parent = nullptr;
|
wxWindow* parent = nullptr;
|
||||||
@ -689,5 +691,57 @@ std::string priv::choose_svg_file()
|
|||||||
return path;
|
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
|
// any existing icon filename to not influence GUI
|
||||||
const std::string GLGizmoSVG::M_ICON_FILENAME = "cut.svg";
|
const std::string GLGizmoSVG::M_ICON_FILENAME = "cut.svg";
|
||||||
|
@ -37,29 +37,29 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||||
/// <param name="mouse_pos">Define position of new volume</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);
|
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||||
void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
|
bool create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos); // first open file dialog
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create new text without given position
|
/// Create new text without given position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||||
void create_volume(const std::string &svg_file_path, ModelVolumeType volume_type);
|
/// <returns>True on succesfull start creation otherwise False</returns>
|
||||||
void create_volume(ModelVolumeType volume_type); // first open file dialog
|
bool create_volume(ModelVolumeType volume_type); // first open file dialog
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check whether volume is object containing only emboss volume
|
/// Check whether volume is object containing only emboss volume
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume">Pointer to volume</param>
|
/// <param name="volume">Pointer to volume</param>
|
||||||
/// <returns>True when object otherwise False</returns>
|
/// <returns>True when object otherwise False</returns>
|
||||||
static bool is_svg_object(const ModelVolume *volume);
|
static bool is_svg_object(const ModelVolume &volume);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check whether volume has emboss data
|
/// Check whether volume has emboss data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume">Pointer to volume</param>
|
/// <param name="volume">Pointer to volume</param>
|
||||||
/// <returns>True when constain emboss data otherwise False</returns>
|
/// <returns>True when constain emboss data otherwise False</returns>
|
||||||
static bool is_svg(const ModelVolume *volume);
|
static bool is_svg(const ModelVolume &volume);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool on_init() override;
|
bool on_init() override;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <libslic3r/Model.hpp>
|
#include <libslic3r/Model.hpp>
|
||||||
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
|
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
|
||||||
#include <libslic3r/CutSurface.hpp> // use surface cuts
|
#include <libslic3r/CutSurface.hpp> // use surface cuts
|
||||||
|
#include <libslic3r/BuildVolume.hpp> // create object
|
||||||
|
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
#include "slic3r/GUI/NotificationManager.hpp"
|
#include "slic3r/GUI/NotificationManager.hpp"
|
||||||
@ -27,6 +28,120 @@ using namespace Slic3r::Emboss;
|
|||||||
using namespace Slic3r::GUI;
|
using namespace Slic3r::GUI;
|
||||||
using namespace Slic3r::GUI::Emboss;
|
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
|
// private namespace
|
||||||
namespace priv{
|
namespace priv{
|
||||||
// create sure that emboss object is bigger than source object [in mm]
|
// create sure that emboss object is bigger than source object [in mm]
|
||||||
@ -37,12 +152,13 @@ constexpr float safe_extension = 1.0f;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input"></param>
|
/// <param name="input"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
|
static bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
|
||||||
bool check(const DataCreateVolume &input, bool is_main_thread = false);
|
static bool check(GLGizmosManager::EType gizmo);
|
||||||
bool check(const DataCreateObject &input);
|
static bool check(const DataCreateVolume &input, bool is_main_thread = false);
|
||||||
bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
|
static bool check(const DataCreateObject &input);
|
||||||
bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
|
static bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
|
||||||
bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
|
static bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||||
|
static bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||||
|
|
||||||
// <summary>
|
// <summary>
|
||||||
/// Try to create mesh from text
|
/// 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="type">Type of new volume</param>
|
||||||
/// <param name="trmat">Transformation of volume inside of object</param>
|
/// <param name="trmat">Transformation of volume inside of object</param>
|
||||||
/// <param name="data">Text configuration and New VolumeName</param>
|
/// <param name="data">Text configuration and New VolumeName</param>
|
||||||
static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id,
|
/// <param name="gizmo">Gizmo to open</param>
|
||||||
const ModelVolumeType type, const Transform3d trmat, const DataBase &data);
|
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>
|
/// <summary>
|
||||||
/// Select Volume from objects
|
/// 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>
|
/// <returns>Extruded object from cuted surace</returns>
|
||||||
static TriangleMesh cut_surface(/*const*/ DataBase &input1, const SurfaceVolumeData &input2, std::function<bool()> was_canceled);
|
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 void create_message(const std::string &message); // only in finalize
|
||||||
static bool process(std::exception_ptr &eptr);
|
static bool process(std::exception_ptr &eptr);
|
||||||
static bool finalize(bool canceled, std::exception_ptr &eptr, const DataBase &input);
|
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
|
}// namespace priv
|
||||||
|
|
||||||
|
void Slic3r::GUI::Emboss::DataBase::write(ModelVolume &volume) const{
|
||||||
|
volume.name = volume_name;
|
||||||
|
volume.emboss_shape = shape;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
/// Create Volume
|
/// Create Volume
|
||||||
CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input): m_input(std::move(input)){ assert(priv::check(m_input, true)); }
|
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;
|
return;
|
||||||
if (m_result.its.empty())
|
if (m_result.its.empty())
|
||||||
return priv::create_message(_u8L("Can't create empty volume."));
|
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());
|
bed_shape_.reserve(m_input.bed_shape.size());
|
||||||
for (const Vec2d &p : m_input.bed_shape)
|
for (const Vec2d &p : m_input.bed_shape)
|
||||||
bed_shape_.emplace_back(p.cast<int>());
|
bed_shape_.emplace_back(p.cast<int>());
|
||||||
Polygon bed(bed_shape_);
|
Slic3r::Polygon bed(bed_shape_);
|
||||||
if (!bed.contains(bed_coor.cast<int>()))
|
if (!bed.contains(bed_coor.cast<int>()))
|
||||||
// mouse pose is out of build plate so create object in center of plate
|
// mouse pose is out of build plate so create object in center of plate
|
||||||
bed_coor = bed.centroid().cast<double>();
|
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
|
// Manager::reset_all() So Gizmo could be closed before end of creation object
|
||||||
GLCanvas3D *canvas = plater->canvas3D();
|
GLCanvas3D *canvas = plater->canvas3D();
|
||||||
GLGizmosManager &manager = canvas->get_gizmos_manager();
|
GLGizmosManager &manager = canvas->get_gizmos_manager();
|
||||||
if (manager.get_current_type() != GLGizmosManager::Emboss)
|
if (manager.get_current_type() != m_input.gizmo)
|
||||||
manager.open_gizmo(GLGizmosManager::Emboss);
|
manager.open_gizmo(m_input.gizmo);
|
||||||
|
|
||||||
// redraw scene
|
// redraw scene
|
||||||
canvas->reload_scene(true);
|
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))
|
if (!priv::finalize(canceled, eptr, *m_input.base))
|
||||||
return;
|
return;
|
||||||
priv::create_volume(std::move(m_result), m_input.object_id,
|
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);
|
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)
|
const ModelVolumePtrs &volumes = text_volume.get_object()->volumes;
|
||||||
return {};
|
|
||||||
if (!text_volume->text_configuration.has_value())
|
|
||||||
return {};
|
|
||||||
const ModelVolumePtrs &volumes = text_volume->get_object()->volumes;
|
|
||||||
// no other volume in object
|
// no other volume in object
|
||||||
if (volumes.size() <= 1)
|
if (volumes.size() <= 1)
|
||||||
return {};
|
return {};
|
||||||
return create_sources(volumes, text_volume->id().id);
|
return priv::create_sources(volumes, text_volume.id().id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start_create_volume_job(
|
bool start_create_volume(Plater *plater_ptr,
|
||||||
Worker &worker, const ModelObject &object, const Transform3d volume_tr, DataBasePtr data, ModelVolumeType volume_type)
|
DataBasePtr data,
|
||||||
{
|
ModelVolumeType volume_type,
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Transform3d> create_volume_transformation_on_surface(const Vec2d &screen_coor,
|
|
||||||
const Camera &camera,
|
|
||||||
const ModelVolume &volume,
|
|
||||||
const ModelInstance &instance,
|
|
||||||
RaycastManager &raycaster,
|
RaycastManager &raycaster,
|
||||||
GLCanvas3D &canvas,
|
unsigned char gizmo,
|
||||||
|
const Vec2d &mouse_pos,
|
||||||
const std::optional<float> &distance,
|
const std::optional<float> &distance,
|
||||||
const std::optional<float> &angle)
|
const std::optional<float> &angle)
|
||||||
{
|
{
|
||||||
auto cond = RaycastManager::AllowVolumes({volume.id().id});
|
if (!priv::is_valid(volume_type))
|
||||||
RaycastManager::Meshes meshes = create_meshes(canvas, cond);
|
return false;
|
||||||
raycaster.actualize(instance, &cond, &meshes);
|
|
||||||
|
|
||||||
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
|
GLCanvas3D *canvas_ptr = plater.get_current_canvas3D();
|
||||||
// object. After right click, object is selected and object_idx is set
|
assert(canvas_ptr);
|
||||||
// also hit must exist. But there is options to add text by object list
|
if (canvas_ptr == nullptr)
|
||||||
if (!hit.has_value())
|
return false;
|
||||||
return {};
|
|
||||||
|
|
||||||
// Create result volume transformation
|
GLGizmosManager::EType gizmo_type = static_cast<GLGizmosManager::EType>(gizmo);
|
||||||
Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, Slic3r::GUI::up_limit);
|
|
||||||
|
|
||||||
apply_transformation(angle, distance, surface_trmat);
|
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);
|
||||||
|
|
||||||
return instance.get_matrix().inverse() * surface_trmat;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform3d create_volume_transformation(const GLVolume &gl_volume, const ModelObjectPtrs &objects, float volume_height, float volume_depth)
|
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)
|
||||||
{
|
{
|
||||||
// Transformation is inspired add generic volumes in ObjectList::load_generic_subobject
|
if (!priv::is_valid(volume_type))
|
||||||
const ModelObject *obj = objects[gl_volume.object_idx()];
|
return false;
|
||||||
BoundingBoxf3 instance_bb = obj->instance_bounding_box(gl_volume.instance_idx());
|
|
||||||
// Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
|
|
||||||
Transform3d tr = gl_volume.get_instance_transformation().get_matrix_no_offset().inverse();
|
|
||||||
Vec3d offset_tr(0, // center of instance - Can't suggest width of text before it will be created
|
|
||||||
- instance_bb.size().y() / 2 - volume_height / 2, // under
|
|
||||||
volume_depth / 2 - instance_bb.size().z() / 2 // lay on bed
|
|
||||||
);
|
|
||||||
return tr * Eigen::Translation3d(offset_tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const GLVolume *find_closest(
|
assert(plater_ptr);
|
||||||
const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center)
|
if (plater_ptr == nullptr)
|
||||||
{
|
return false;
|
||||||
assert(closest_center != nullptr);
|
Plater &plater = *plater_ptr;
|
||||||
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();
|
GLCanvas3D *canvas_ptr = plater.get_current_canvas3D();
|
||||||
for (unsigned int id : indices) {
|
assert(canvas_ptr);
|
||||||
const GLVolume *gl_volume = selection.get_volume(id);
|
if (canvas_ptr == nullptr)
|
||||||
const ModelVolume *volume = get_model_volume(*gl_volume, objects);
|
return false;
|
||||||
if (!volume->is_model_part())
|
const GLCanvas3D &canvas = *canvas_ptr;
|
||||||
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();
|
// select position by camera position and view direction
|
||||||
if (center_sq_distance < distance)
|
const Selection &selection = canvas.get_selection();
|
||||||
continue;
|
int object_idx = selection.get_object_idx();
|
||||||
center_sq_distance = distance;
|
|
||||||
|
|
||||||
*closest_center = c;
|
Size s = canvas.get_canvas_size();
|
||||||
closest = gl_volume;
|
Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
|
||||||
}
|
const ModelObjectPtrs &objects = selection.get_model()->objects;
|
||||||
return closest;
|
|
||||||
|
GLGizmosManager::EType gizmo_type = static_cast<GLGizmosManager::EType>(gizmo);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Is successfull created job for add volume on surface?
|
||||||
|
if (success)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 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
|
} // 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;
|
//res &= input.text_configuration.style.prop.use_surface == use_surface;
|
||||||
return res;
|
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 priv::check(const DataCreateVolume &input, bool is_main_thread) {
|
||||||
bool check_fontfile = false;
|
bool check_fontfile = false;
|
||||||
assert(input.base != nullptr);
|
assert(input.base != nullptr);
|
||||||
@ -504,6 +701,7 @@ bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
|
|||||||
res &= input.volume_type != ModelVolumeType::INVALID;
|
res &= input.volume_type != ModelVolumeType::INVALID;
|
||||||
assert(input.object_id.id >= 0);
|
assert(input.object_id.id >= 0);
|
||||||
res &= input.object_id.id >= 0;
|
res &= input.object_id.id >= 0;
|
||||||
|
res &= check(input.gizmo);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
bool priv::check(const DataCreateObject &input) {
|
bool priv::check(const DataCreateObject &input) {
|
||||||
@ -517,6 +715,7 @@ bool priv::check(const DataCreateObject &input) {
|
|||||||
res &= input.screen_coor.y() >= 0.;
|
res &= input.screen_coor.y() >= 0.;
|
||||||
assert(input.bed_shape.size() >= 3); // at least triangle
|
assert(input.bed_shape.size() >= 3); // at least triangle
|
||||||
res &= input.bed_shape.size() >= 3;
|
res &= input.bed_shape.size() >= 3;
|
||||||
|
res &= check(input.gizmo);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){
|
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);
|
res &= check(*input.base, is_main_thread, use_surface);
|
||||||
assert(!input.sources.empty());
|
assert(!input.sources.empty());
|
||||||
res &= !input.sources.empty();
|
res &= !input.sources.empty();
|
||||||
|
res &= check(input.gizmo);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
bool priv::check(const UpdateSurfaceVolumeData &input, bool is_main_thread){
|
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);
|
UpdateJob::update_volume(volume, std::move(mesh), *data.base);
|
||||||
}
|
}
|
||||||
|
|
||||||
void priv::create_volume(
|
void priv::create_volume(TriangleMesh &&mesh,
|
||||||
TriangleMesh &&mesh, const ObjectID& object_id,
|
const ObjectID &object_id,
|
||||||
const ModelVolumeType type, const Transform3d trmat, const DataBase &data)
|
const ModelVolumeType type,
|
||||||
|
const std::optional<Transform3d> &trmat,
|
||||||
|
const DataBase &data,
|
||||||
|
GLGizmosManager::EType gizmo)
|
||||||
{
|
{
|
||||||
GUI_App &app = wxGetApp();
|
GUI_App &app = wxGetApp();
|
||||||
Plater *plater = app.plater();
|
Plater *plater = app.plater();
|
||||||
@ -741,6 +944,13 @@ void priv::create_volume(
|
|||||||
|
|
||||||
plater->take_snapshot(_L("Add Emboss text 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 !!!
|
// NOTE: be carefull add volume also center mesh !!!
|
||||||
// So first add simple shape(convex hull is also calculated)
|
// So first add simple shape(convex hull is also calculated)
|
||||||
ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type);
|
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->source.is_from_builtin_objects = true;
|
||||||
|
|
||||||
volume->name = data.volume_name; // copy
|
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);
|
data.write(*volume);
|
||||||
|
|
||||||
// update printable state on canvas
|
// update printable state on canvas
|
||||||
@ -935,6 +1162,26 @@ TriangleMesh priv::cut_surface(DataBase& base, const SurfaceVolumeData& input2,
|
|||||||
return TriangleMesh(std::move(new_its));
|
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) {
|
bool priv::process(std::exception_ptr &eptr) {
|
||||||
if (!eptr) return false;
|
if (!eptr) return false;
|
||||||
try {
|
try {
|
||||||
@ -956,6 +1203,141 @@ bool priv::finalize(bool canceled, std::exception_ptr &eptr, const DataBase &inp
|
|||||||
return !process(eptr);
|
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>
|
#include <wx/msgdlg.h>
|
||||||
|
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
#include "libslic3r/Emboss.hpp"
|
#include "libslic3r/Emboss.hpp"
|
||||||
#include "libslic3r/EmbossShape.hpp"
|
#include "libslic3r/EmbossShape.hpp"
|
||||||
#include "libslic3r/Point.hpp" // Transform3d
|
#include "libslic3r/Point.hpp" // Transform3d
|
||||||
|
#include "libslic3r/ObjectID.hpp"
|
||||||
#include "slic3r/Utils/RaycastManager.hpp"
|
|
||||||
|
|
||||||
#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
|
#include "slic3r/GUI/Jobs/EmbossJob.hpp" // Emboss::DataBase
|
||||||
#include "slic3r/GUI/Camera.hpp"
|
#include "slic3r/GUI/Camera.hpp"
|
||||||
@ -17,16 +16,12 @@
|
|||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
class GLVolume;
|
|
||||||
class ModelVolume;
|
|
||||||
class ModelObject;
|
|
||||||
class TriangleMesh;
|
class TriangleMesh;
|
||||||
typedef std::vector<ModelObject *> ModelObjectPtrs;
|
class ModelVolume;
|
||||||
typedef std::vector<ModelVolume *> ModelVolumePtrs;
|
enum class ModelVolumeType : int;
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
class Selection;
|
|
||||||
class RaycastManager;
|
class RaycastManager;
|
||||||
class Worker;
|
class Plater;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
namespace Slic3r::GUI::Emboss {
|
namespace Slic3r::GUI::Emboss {
|
||||||
@ -38,7 +33,7 @@ class DataBase
|
|||||||
{
|
{
|
||||||
public:
|
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) : 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))
|
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape))
|
||||||
{}
|
{}
|
||||||
virtual ~DataBase() {}
|
virtual ~DataBase() {}
|
||||||
@ -54,11 +49,7 @@ public:
|
|||||||
/// Write data how to reconstruct shape to volume
|
/// Write data how to reconstruct shape to volume
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volume">Data object for store emboss params</param>
|
/// <param name="volume">Data object for store emboss params</param>
|
||||||
virtual void write(ModelVolume &volume) const
|
virtual void write(ModelVolume &volume) const;
|
||||||
{
|
|
||||||
volume.name = volume_name;
|
|
||||||
volume.emboss_shape = shape;
|
|
||||||
};
|
|
||||||
|
|
||||||
// new volume name
|
// new volume name
|
||||||
std::string volume_name;
|
std::string volume_name;
|
||||||
@ -70,77 +61,7 @@ public:
|
|||||||
// shape to emboss
|
// shape to emboss
|
||||||
EmbossShape shape;
|
EmbossShape shape;
|
||||||
};
|
};
|
||||||
|
using DataBasePtr = std::unique_ptr<DataBase>;
|
||||||
/// <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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hold neccessary data to update embossed text object in job
|
/// Hold neccessary data to update embossed text object in job
|
||||||
@ -148,7 +69,7 @@ public:
|
|||||||
struct DataUpdate
|
struct DataUpdate
|
||||||
{
|
{
|
||||||
// Hold data about shape
|
// Hold data about shape
|
||||||
std::unique_ptr<DataBase> base;
|
DataBasePtr base;
|
||||||
|
|
||||||
// unique identifier of volume to change
|
// unique identifier of volume to change
|
||||||
ObjectID volume_id;
|
ObjectID volume_id;
|
||||||
@ -212,35 +133,6 @@ struct SurfaceVolumeData
|
|||||||
ModelSources sources;
|
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>
|
/// <summary>
|
||||||
/// Hold neccessary data to update embossed text object in job
|
/// Hold neccessary data to update embossed text object in job
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -264,72 +156,43 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copied triangles from object to be able create mesh for cut surface from
|
/// Copied triangles from object to be able create mesh for cut surface from
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volumes">Source object volumes for cut surface from</param>
|
/// <param name="volume">Define embossed volume</param>
|
||||||
/// <param name="text_volume_id">Source volume id</param>
|
|
||||||
/// <returns>Source data for cut surface from</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Copied triangles from object to be able create mesh for cut surface from
|
/// Create new volume on position of mouse cursor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text_volume">Define text in object</param>
|
/// <param name="plater_ptr">canvas + camera + bed shape + </param>
|
||||||
/// <returns>Source data for cut surface from</returns>
|
/// <param name="data">Shape of emboss</param>
|
||||||
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume);
|
/// <param name="volume_type">New created volume type</param>
|
||||||
|
/// <param name="raycaster">Knows object in scene</param>
|
||||||
using DataBasePtr = std::unique_ptr<DataBase>;
|
/// <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>
|
||||||
/// <summary>
|
/// <param name="distance">Wanted additionl move in Z(emboss) direction of new created volume</param>
|
||||||
/// Start job for add new volume to object with given transformation
|
/// <param name="angle">Wanted additionl rotation around Z of new created volume</param>
|
||||||
/// </summary>
|
/// <returns>True on success otherwise False</returns>
|
||||||
/// <param name="worker">Define where to queue the job. e.g. wxGetApp().plater()->get_ui_job_worker()</param>
|
bool start_create_volume(Plater *plater_ptr,
|
||||||
/// <param name="object">Define where to add</param>
|
DataBasePtr data,
|
||||||
/// <param name="volume_tr">Wanted volume transformation</param>
|
ModelVolumeType volume_type,
|
||||||
/// <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,
|
RaycastManager &raycaster,
|
||||||
GLCanvas3D &canvas,
|
unsigned char gizmo,
|
||||||
|
const Vec2d &mouse_pos,
|
||||||
const std::optional<float> &distance = {},
|
const std::optional<float> &distance = {},
|
||||||
const std::optional<float> &angle = {});
|
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>
|
/// <summary>
|
||||||
/// Find volume in selected objects with closest convex hull to screen center.
|
/// Same as previous function but without mouse position
|
||||||
|
/// Need to suggest position or put near the selection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="selection">Define where to search for closest</param>
|
bool start_create_volume_without_position(Plater *plater_ptr,
|
||||||
/// <param name="screen_center">Canvas center(dependent on camera settings)</param>
|
DataBasePtr data,
|
||||||
/// <param name="objects">Actual objects</param>
|
ModelVolumeType volume_type,
|
||||||
/// <param name="closest_center">OUT: coordinate of controid of closest volume</param>
|
RaycastManager &raycaster,
|
||||||
/// <returns>closest volume when exists otherwise nullptr</returns>
|
unsigned char gizmo,
|
||||||
const GLVolume *find_closest(
|
const std::optional<float> &distance = {},
|
||||||
const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center);
|
const std::optional<float> &angle = {});
|
||||||
|
|
||||||
} // namespace Slic3r::GUI
|
} // namespace Slic3r::GUI
|
||||||
|
|
||||||
#endif // slic3r_EmbossJob_hpp_
|
#endif // slic3r_EmbossJob_hpp_
|
||||||
|
Loading…
x
Reference in New Issue
Block a user