Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_show_sla_supports

This commit is contained in:
enricoturri1966 2023-03-22 11:00:32 +01:00
commit ddf636fe38
32 changed files with 2409 additions and 1439 deletions

View File

@ -1,4 +1,5 @@
min_slic3r_version = 2.6.0-alpha5
1.9.0-alpha0 Updated output filename format.
1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL).
1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%.
min_slic3r_version = 2.6.0-alpha1

View File

@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.7.0-alpha2
config_version = 1.9.0-alpha0
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -192,7 +192,7 @@ notes =
overhangs = 1
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode
perimeters = 2
perimeter_extruder = 1
perimeter_extrusion_width = 0.45
@ -390,7 +390,7 @@ support_material_xy_spacing = 80%
support_material_interface_spacing = 0.2
support_material_spacing = 2.2
raft_first_layer_expansion = 2
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode
infill_anchor = 2
infill_anchor_max = 15
thick_bridges = 0

View File

@ -165,9 +165,9 @@ inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(si
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
// during tree traversal.
template<typename LineType>
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
inline AABBTreeIndirect::Tree<LineType::Dim, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
{
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
using TreeType = AABBTreeIndirect::Tree<LineType::Dim, typename LineType::Scalar>;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
using BoundingBox = typename TreeType::BoundingBox;

View File

@ -1264,15 +1264,17 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
return result;
}
void Emboss::apply_transformation(const FontProp &font_prop,
Transform3d &transformation)
{
if (font_prop.angle.has_value()) {
double angle_z = *font_prop.angle;
void Emboss::apply_transformation(const FontProp &font_prop, Transform3d &transformation){
apply_transformation(font_prop.angle, font_prop.distance, transformation);
}
void Emboss::apply_transformation(const std::optional<float>& angle, const std::optional<float>& distance, Transform3d &transformation) {
if (angle.has_value()) {
double angle_z = *angle;
transformation *= Eigen::AngleAxisd(angle_z, Vec3d::UnitZ());
}
if (font_prop.distance.has_value()) {
Vec3d translate = Vec3d::UnitZ() * (*font_prop.distance);
if (distance.has_value()) {
Vec3d translate = Vec3d::UnitZ() * (*distance);
transformation.translate(translate);
}
}
@ -1540,60 +1542,95 @@ std::optional<Vec2d> Emboss::ProjectZ::unproject(const Vec3d &p, double *depth)
return Vec2d(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE);
}
Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
const Vec3f &normal,
float up_limit)
Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit)
{
// up and emboss direction for generated model
Vec3d text_up_dir = Vec3d::UnitY();
Vec3d text_emboss_dir = Vec3d::UnitZ();
// Normal must be 1
assert(is_approx(normal.squaredNorm(), 1.));
// wanted up direction of result
Vec3d wanted_up_side = Vec3d::UnitZ();
if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY();
Vec3d wanted_emboss_dir = normal.cast<double>();
// after cast from float it needs to be normalized again
wanted_emboss_dir.normalize();
Vec3d wanted_up_side =
(std::fabs(normal.z()) > up_limit)?
Vec3d::UnitY() : Vec3d::UnitZ();
// create perpendicular unit vector to surface triangle normal vector
// lay on surface of triangle and define up vector for text
Vec3d wanted_up_dir = wanted_emboss_dir
.cross(wanted_up_side)
.cross(wanted_emboss_dir);
Vec3d wanted_up_dir = normal.cross(wanted_up_side).cross(normal);
// normal3d is NOT perpendicular to normal_up_dir
wanted_up_dir.normalize();
wanted_up_dir.normalize();
return wanted_up_dir;
}
std::optional<float> Emboss::calc_up(const Transform3d &tr, double up_limit)
{
auto tr_linear = tr.linear();
// z base of transformation ( tr * UnitZ )
Vec3d normal = tr_linear.col(2);
// scaled matrix has base with different size
normal.normalize();
Vec3d suggested = suggest_up(normal);
assert(is_approx(suggested.squaredNorm(), 1.));
Vec3d up = tr_linear.col(1); // tr * UnitY()
up.normalize();
double dot = suggested.dot(up);
if (dot >= 1. || dot <= -1.)
return {}; // zero angle
Matrix3d m;
m.row(0) = up;
m.row(1) = suggested;
m.row(2) = normal;
double det = m.determinant();
return -atan2(det, dot);
}
Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position,
const Vec3d &normal,
double up_limit)
{
// is normalized ?
assert(is_approx(normal.squaredNorm(), 1.));
// up and emboss direction for generated model
Vec3d up_dir = Vec3d::UnitY();
Vec3d emboss_dir = Vec3d::UnitZ();
// after cast from float it needs to be normalized again
Vec3d wanted_up_dir = suggest_up(normal, up_limit);
// perpendicular to emboss vector of text and normal
Vec3d axis_view;
double angle_view;
if (wanted_emboss_dir == -Vec3d::UnitZ()) {
if (normal == -Vec3d::UnitZ()) {
// text_emboss_dir has opposit direction to wanted_emboss_dir
axis_view = Vec3d::UnitY();
angle_view = M_PI;
} else {
axis_view = text_emboss_dir.cross(wanted_emboss_dir);
angle_view = std::acos(text_emboss_dir.dot(wanted_emboss_dir)); // in rad
axis_view = emboss_dir.cross(normal);
angle_view = std::acos(emboss_dir.dot(normal)); // in rad
axis_view.normalize();
}
Eigen::AngleAxis view_rot(angle_view, axis_view);
Vec3d wanterd_up_rotated = view_rot.matrix().inverse() * wanted_up_dir;
wanterd_up_rotated.normalize();
double angle_up = std::acos(text_up_dir.dot(wanterd_up_rotated));
double angle_up = std::acos(up_dir.dot(wanterd_up_rotated));
// text_view and text_view2 should have same direction
Vec3d text_view2 = text_up_dir.cross(wanterd_up_rotated);
Vec3d diff_view = text_emboss_dir - text_view2;
Vec3d text_view = up_dir.cross(wanterd_up_rotated);
Vec3d diff_view = emboss_dir - text_view;
if (std::fabs(diff_view.x()) > 1. ||
std::fabs(diff_view.y()) > 1. ||
std::fabs(diff_view.z()) > 1.) // oposit direction
angle_up *= -1.;
Eigen::AngleAxis up_rot(angle_up, text_emboss_dir);
Eigen::AngleAxis up_rot(angle_up, emboss_dir);
Transform3d transform = Transform3d::Identity();
transform.translate(position.cast<double>());
transform.translate(position);
transform.rotate(view_rot);
transform.rotate(up_rot);
return transform;

View File

@ -193,6 +193,7 @@ namespace Emboss
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
/// <param name="transformation">In / Out transformation to modify by property</param>
void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
void apply_transformation(const std::optional<float> &angle, const std::optional<float> &distance, Transform3d &transformation);
/// <summary>
/// Read information from naming table of font file
@ -273,7 +274,23 @@ namespace Emboss
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
/// <returns>Projected shape into space</returns>
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
/// <summary>
/// Suggest wanted up vector of embossed text by emboss direction
/// </summary>
/// <param name="normal">Normalized vector of emboss direction in world</param>
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
/// <returns>Wanted up vector</returns>
Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9);
/// <summary>
/// By transformation calculate angle between suggested and actual up vector
/// </summary>
/// <param name="tr">Transformation of embossed volume in world</param>
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
/// <returns>Rotation of suggested up-vector[in rad] in the range [-Pi, Pi], When rotation is not zero</returns>
std::optional<float> calc_up(const Transform3d &tr, double up_limit = 0.9);
/// <summary>
/// Create transformation for emboss text object to lay on surface point
/// </summary>
@ -282,7 +299,7 @@ namespace Emboss
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
/// <returns>Transformation onto surface point</returns>
Transform3d create_transformation_onto_surface(
const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f);
const Vec3d &position, const Vec3d &normal, double up_limit = 0.9);
class ProjectZ : public IProjection
{

View File

@ -1397,6 +1397,25 @@ void ModelObject::clone_for_cut(ModelObject** obj)
(*obj)->input_file.clear();
}
bool ModelVolume::is_the_only_one_part() const
{
if (m_type != ModelVolumeType::MODEL_PART)
return false;
if (object == nullptr)
return false;
for (const ModelVolume *v : object->volumes) {
if (v == nullptr)
continue;
// is this volume?
if (v->id() == this->id())
continue;
// exist another model part in object?
if (v->type() == ModelVolumeType::MODEL_PART)
return false;
}
return true;
}
void ModelVolume::reset_extra_facets()
{
this->supported_facets.reset();

View File

@ -835,6 +835,7 @@ public:
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
bool is_text() const { return text_configuration.has_value(); }
bool is_the_only_one_part() const; // behave like an object
t_model_material_id material_id() const { return m_material_id; }
void reset_extra_facets();
void apply_tolerance();

View File

@ -587,7 +587,7 @@ namespace client
param1.set_s(buf);
}
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
static void regex_op(const expr &lhs, boost::iterator_range<Iterator> &rhs, char op, expr &out)
{
const std::string *subject = nullptr;
if (lhs.type() == TYPE_STRING) {
@ -601,7 +601,7 @@ namespace client
bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern));
if (op == '!')
result = ! result;
lhs.set_b(result);
out.set_b(result);
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) {
// Syntax error in the regular expression
boost::throw_exception(qi::expectation_failure<Iterator>(
@ -609,8 +609,37 @@ namespace client
}
}
static void regex_matches (expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '='); }
static void regex_doesnt_match(expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '!'); }
static void regex_matches (expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '=', lhs); }
static void regex_doesnt_match(expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '!', lhs); }
static void one_of_test_init(expr &out) {
out.set_b(false);
}
template<bool RegEx>
static void one_of_test(const expr &match, const expr &pattern, expr &out) {
if (! out.b()) {
if (match.type() != TYPE_STRING)
match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value");
if (pattern.type() != TYPE_STRING)
match.throw_exception("one_of(): Pattern has to be a string value");
if (RegEx) {
try {
out.set_b(SLIC3R_REGEX_NAMESPACE::regex_match(match.s(), SLIC3R_REGEX_NAMESPACE::regex(pattern.s())));
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &) {
// Syntax error in the regular expression
pattern.throw_exception("Regular expression compilation failed");
}
} else
out.set_b(match.s() == pattern.s());
}
}
static void one_of_test_regex(const expr &match, boost::iterator_range<Iterator> &pattern, expr &out) {
if (! out.b()) {
if (match.type() != TYPE_STRING)
match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value");
regex_op(match, pattern, '=', out);
}
}
static void logical_op(expr &lhs, expr &rhs, char op)
{
@ -1101,6 +1130,7 @@ namespace client
{ "multiplicative_expression", "Expecting an expression." },
{ "unary_expression", "Expecting an expression." },
{ "optional_parameter", "Expecting a closing brace or an optional parameter." },
{ "one_of_list", "Expecting a list of string patterns (simple text or rexep)" },
{ "variable_reference", "Expecting a variable reference."},
{ "is_nil_test", "Expecting a scalar variable reference."},
{ "variable", "Expecting a variable name."},
@ -1221,6 +1251,7 @@ namespace client
qi::_a_type _a;
qi::_b_type _b;
qi::_r1_type _r1;
qi::_r2_type _r2;
// Starting symbol of the grammer.
// The leading eps is required by the "expectation point" operator ">".
@ -1395,7 +1426,8 @@ namespace client
[ px::bind(&expr<Iterator>::template digits<true>, _val, _2, _3) ]
| (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1]
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ]
| (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
@ -1404,6 +1436,20 @@ namespace client
);
unary_expression.name("unary_expression");
one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2];
one_of.name("one_of");
one_of_list =
eps[px::bind(&expr<Iterator>::one_of_test_init, _val)] >
( ',' > *(
(
unary_expression(_r1)[px::bind(&expr<Iterator>::template one_of_test<false>, _r2, _1, _val)]
| (lit('~') > unary_expression(_r1))[px::bind(&expr<Iterator>::template one_of_test<true>, _r2, _1, _val)]
| regular_expression[px::bind(&expr<Iterator>::one_of_test_regex, _r2, _1, _val)]
) >> -lit(','))
| eps
);
one_of_list.name("one_of_list");
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
@ -1445,6 +1491,7 @@ namespace client
("random")
("round")
("not")
("one_of")
("or")
("true");
@ -1466,6 +1513,8 @@ namespace client
debug(additive_expression);
debug(multiplicative_expression);
debug(unary_expression);
debug(one_of);
debug(one_of_list);
debug(optional_parameter);
debug(variable_reference);
debug(variable);
@ -1517,6 +1566,9 @@ namespace client
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit_encoding::space_type> variable;
// Evaluating whether a nullable variable is nil.
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit_encoding::space_type> is_nil_test;
// Evaluating "one of" list of patterns.
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> one_of;
qi::rule<Iterator, expr<Iterator>(const MyContext*, const expr<Iterator> &param), spirit_encoding::space_type> one_of_list;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;

View File

@ -49,10 +49,12 @@ struct FontProp
// used for move over model surface
// When not set value is zero and is not stored
std::optional<float> distance; // [in mm]
// change up vector direction of font
// Angle of rotation around emboss direction (Z axis)
// It is calculate on the fly from volume world transformation
// only StyleManager keep actual value for comparision with style
// When not set value is zero and is not stored
std::optional<float> angle; // [in radians]
std::optional<float> angle; // [in radians] form -Pi to Pi
// Parameter for True Type Font collections
// Select index of font in collection

View File

@ -97,6 +97,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_Geometry.hpp
GUI/I18N.cpp
GUI/I18N.hpp
GUI/IconManager.cpp
GUI/IconManager.hpp
GUI/MainFrame.cpp
GUI/MainFrame.hpp
GUI/Plater.cpp
@ -157,6 +159,8 @@ set(SLIC3R_GUI_SOURCES
GUI/RemovableDriveManager.hpp
GUI/SendSystemInfoDialog.cpp
GUI/SendSystemInfoDialog.hpp
GUI/SurfaceDrag.cpp
GUI/SurfaceDrag.hpp
GUI/BonjourDialog.cpp
GUI/BonjourDialog.hpp
GUI/ButtonsDescription.cpp

View File

@ -1407,7 +1407,6 @@ void GLCanvas3D::enable_legend_texture(bool enable)
void GLCanvas3D::enable_picking(bool enable)
{
m_picking_enabled = enable;
m_selection.set_mode(Selection::Instance);
}
void GLCanvas3D::enable_moving(bool enable)
@ -7335,5 +7334,95 @@ const ModelVolume *get_model_volume(const GLVolume &v, const Model &model)
return ret;
}
const ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects)
{
for (const ModelObject *obj : objects)
for (const ModelVolume *vol : obj->volumes)
if (vol->id() == volume_id)
return vol;
return nullptr;
}
ModelVolume *get_model_volume(const GLVolume &v, const ModelObject& object) {
if (v.volume_idx() < 0)
return nullptr;
size_t volume_idx = static_cast<size_t>(v.volume_idx());
if (volume_idx >= object.volumes.size())
return nullptr;
return object.volumes[volume_idx];
}
ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects)
{
if (v.object_idx() < 0)
return nullptr;
size_t objext_idx = static_cast<size_t>(v.object_idx());
if (objext_idx >= objects.size())
return nullptr;
if (objects[objext_idx] == nullptr)
return nullptr;
return get_model_volume(v, *objects[objext_idx]);
}
GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas) {
int hovered_id_signed = canvas.get_first_hover_volume_idx();
if (hovered_id_signed < 0)
return nullptr;
size_t hovered_id = static_cast<size_t>(hovered_id_signed);
const GLVolumePtrs &volumes = canvas.get_volumes().volumes;
if (hovered_id >= volumes.size())
return nullptr;
return volumes[hovered_id];
}
GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas) {
const GLVolume *gl_volume = get_selected_gl_volume(canvas.get_selection());
if (gl_volume == nullptr)
return nullptr;
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
for (GLVolume *v : gl_volumes)
if (v->composite_id == gl_volume->composite_id)
return v;
return nullptr;
}
ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model) {
return get_model_object(gl_volume, model.objects);
}
ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects) {
if (gl_volume.object_idx() < 0)
return nullptr;
size_t objext_idx = static_cast<size_t>(gl_volume.object_idx());
if (objext_idx >= objects.size())
return nullptr;
return objects[objext_idx];
}
ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model& model) {
return get_model_instance(gl_volume, model.objects);
}
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects) {
if (gl_volume.instance_idx() < 0)
return nullptr;
ModelObject *object = get_model_object(gl_volume, objects);
return get_model_instance(gl_volume, *object);
}
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object) {
if (gl_volume.instance_idx() < 0)
return nullptr;
size_t instance_idx = static_cast<size_t>(gl_volume.instance_idx());
if (instance_idx >= object.instances.size())
return nullptr;
return object.instances[instance_idx];
}
} // namespace GUI
} // namespace Slic3r

View File

@ -1068,7 +1068,20 @@ private:
float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); }
};
const ModelVolume * get_model_volume(const GLVolume &v, const Model &model);
const ModelVolume *get_model_volume(const GLVolume &v, const Model &model);
const ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects);
ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects);
ModelVolume *get_model_volume(const GLVolume &v, const ModelObject &object);
GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas);
GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas);
ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model);
ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects);
ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model &model);
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects);
ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object);
} // namespace GUI
} // namespace Slic3r

View File

@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig
m_canvas->allow_multisample(OpenGLManager::can_multisample());
m_canvas->enable_picking(true);
m_canvas->get_selection().set_mode(Selection::Instance);
m_canvas->enable_moving(true);
// XXX: more config from 3D.pm
m_canvas->set_model(model);

File diff suppressed because it is too large Load Diff

View File

@ -5,15 +5,13 @@
// which overrides our localization "L" macro.
#include "GLGizmoBase.hpp"
#include "GLGizmoRotate.hpp"
#include "slic3r/GUI/GLTexture.hpp"
#include "slic3r/GUI/IconManager.hpp"
#include "slic3r/GUI/SurfaceDrag.hpp"
#include "slic3r/Utils/RaycastManager.hpp"
#include "slic3r/Utils/EmbossStyleManager.hpp"
#include "admesh/stl.h" // indexed_triangle_set
#include <optional>
#include <memory>
#include <mutex>
#include <thread>
#include <atomic>
#include "libslic3r/Emboss.hpp"
@ -28,12 +26,10 @@ class wxFont;
namespace Slic3r{
class AppConfig;
class GLVolume;
enum class ModelVolumeType : int;
}
namespace Slic3r::GUI {
class MeshRaycaster;
class GLGizmoEmboss : public GLGizmoBase
{
public:
@ -82,14 +78,12 @@ protected:
std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); }
std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); }
private:
void initialize();
static EmbossStyles create_default_styles();
// localized default text
void set_default_text();
void set_volume_by_selection();
// load text configuration from volume into gizmo
bool set_volume(ModelVolume *volume);
void reset_volume();
// create volume from text - main functionality
bool process();
@ -108,17 +102,21 @@ private:
void init_font_name_texture();
struct FaceName;
void draw_font_preview(FaceName &face, bool is_visible);
void draw_font_list_line();
void draw_font_list();
void draw_style_edit();
void draw_height(bool use_inch);
void draw_depth(bool use_inch);
// call after set m_style_manager.get_style().prop.size_in_mm
bool set_height();
// call after set m_style_manager.get_style().prop.emboss
bool set_depth();
bool draw_italic_button();
bool draw_bold_button();
void draw_advanced();
bool select_facename(const wxString& facename);
void init_face_names();
void do_translate(const Vec3d& relative_move);
void do_rotate(float relative_z_angle);
@ -145,7 +143,7 @@ private:
template<typename T, typename Draw>
bool revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw);
bool m_should_set_minimal_windows_size = false;
bool m_should_set_minimal_windows_size = false;
void set_minimal_window_size(bool is_advance_edit_style);
ImVec2 get_minimal_window_size() const;
@ -153,41 +151,43 @@ private:
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
bool on_mouse_for_translate(const wxMouseEvent &mouse_event);
bool choose_font_by_wxdialog();
bool choose_true_type_file();
bool choose_svg_file();
// When open text loaded from .3mf it could be written with unknown font
bool m_is_unknown_font;
void create_notification_not_valid_font(const TextConfiguration& tc);
void create_notification_not_valid_font(const std::string& text);
void remove_notification_not_valid_font();
// This configs holds GUI layout size given by translated texts.
// etc. When language changes, GUI is recreated and this class constructed again,
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
struct GuiCfg
{
// Detect invalid config values when change monitor DPI
double screen_scale;
float main_toolbar_height;
// Zero means it is calculated in init function
ImVec2 minimal_window_size = ImVec2(0, 0);
ImVec2 minimal_window_size_with_advance = ImVec2(0, 0);
ImVec2 minimal_window_size_with_collections = ImVec2(0, 0);
float height_of_volume_type_selector = 0.f;
float input_width = 0.f;
float delete_pos_x = 0.f;
float max_style_name_width = 0.f;
unsigned int icon_width = 0;
ImVec2 minimal_window_size = ImVec2(0, 0);
ImVec2 minimal_window_size_with_advance = ImVec2(0, 0);
ImVec2 minimal_window_size_with_collections = ImVec2(0, 0);
float height_of_volume_type_selector = 0.f;
float input_width = 0.f;
float delete_pos_x = 0.f;
float max_style_name_width = 0.f;
unsigned int icon_width = 0;
// maximal width and height of style image
Vec2i max_style_image_size = Vec2i(0, 0);
float indent = 0.f;
float input_offset = 0.f;
float advanced_input_offset = 0.f;
ImVec2 text_size;
// maximal size of face name image
Vec2i face_name_size = Vec2i(100, 0);
float face_name_max_width = 100.f;
Vec2i face_name_size = Vec2i(100, 0);
float face_name_max_width = 100.f;
float face_name_texture_offset_x = 105.f;
// maximal texture generate jobs running at once
@ -197,7 +197,7 @@ private:
struct Translations
{
std::string font;
std::string size;
std::string height;
std::string depth;
std::string use_surface;
@ -205,16 +205,18 @@ private:
std::string char_gap;
std::string line_gap;
std::string boldness;
std::string italic;
std::string surface_distance;
std::string angle;
std::string skew_ration;
std::string from_surface;
std::string rotation;
std::string keep_up;
std::string collection;
};
Translations translations;
GuiCfg() = default;
};
std::optional<const GuiCfg> m_gui_cfg;
std::optional<const GuiCfg> m_gui_cfg;
static GuiCfg create_gui_configuration();
// Is open tree with advanced options
bool m_is_advanced_edit_style = false;
// when true window will appear near to text volume when open
@ -223,6 +225,7 @@ private:
// setted only when wanted to use - not all the time
std::optional<ImVec2> m_set_window_offset;
// Keep information about stored styles and loaded actual style to compare with
Emboss::StyleManager m_style_manager;
struct FaceName{
@ -244,13 +247,15 @@ private:
// true .. already enumerated(During opened combo box)
bool is_init = false;
bool has_truncated_names = false;
// data of can_load() faces
std::vector<FaceName> faces = {};
// Sorter set of Non valid face names in OS
std::vector<wxString> bad = {};
// Configuration of font encoding
const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
// Identify if preview texture exists
GLuint texture_id = 0;
@ -277,11 +282,17 @@ private:
static bool store(const Facenames &facenames);
static bool load(Facenames &facenames);
static void init_face_names(Facenames &facenames);
static void init_truncated_names(Facenames &face_names, float max_width);
// Text to emboss
std::string m_text;
std::string m_text; // Sequence of Unicode UTF8 symbols
// actual volume
// When true keep up vector otherwise relative rotation
bool m_keep_up = true;
// current selected volume
// NOTE: Be carefull could be uninitialized (removed from Model)
ModelVolume *m_volume;
// When work with undo redo stack there could be situation that
@ -299,59 +310,25 @@ private:
// Value is set only when dragging rotation to calculate actual angle
std::optional<float> m_rotate_start_angle;
// when draging with text object hold screen offset of cursor from object center
std::optional<Vec2d> m_dragging_mouse_offset;
// Keep data about dragging only during drag&drop
std::optional<SurfaceDrag> m_surface_drag;
// TODO: it should be accessible by other gizmo too.
// May be move to plater?
RaycastManager m_raycast_manager;
// Only when drag text object it stores world position
std::optional<Transform3d> m_temp_transformation;
// For text on scaled objects
std::optional<float> m_scale_height;
std::optional<float> m_scale_depth;
void calculate_scale();
// drawing icons
GLTexture m_icons_texture;
IconManager m_icon_manager;
IconManager::VIcons m_icons;
void init_icons();
enum class IconType : unsigned {
rename = 0,
erase,
add,
save,
undo,
italic,
unitalic,
bold,
unbold,
system_selector,
open_file,
// automatic calc of icon's count
_count
};
enum class IconState: unsigned { activable = 0, hovered /*1*/, disabled /*2*/};
void draw_icon(IconType icon, IconState state, ImVec2 size = ImVec2(0,0));
void draw_transparent_icon();
bool draw_clickable(IconType icon, IconState state, IconType hover_icon, IconState hover_state);
bool draw_button(IconType icon, bool disable = false);
// only temporary solution
static const std::string M_ICON_FILENAME;
public:
/// <summary>
/// Check if text is last solid part of object
/// TODO: move to emboss gui utils
/// </summary>
/// <param name="text">Model volume of Text</param>
/// <returns>True when object otherwise False</returns>
static bool is_text_object(const ModelVolume *text);
// TODO: move to file utils
static std::string get_file_name(const std::string &file_path);
};
} // namespace Slic3r::GUI

View File

@ -0,0 +1,204 @@
#include "IconManager.hpp"
#include <cmath>
#include <boost/log/trivial.hpp>
using namespace Slic3r::GUI;
namespace priv {
// set shared pointer to point on bad texture
static void clear(IconManager::Icons &icons);
static const std::vector<std::pair<int, bool>>& get_states(IconManager::RasterType type);
static void draw_transparent_icon(const IconManager::Icon &icon); // only help function
}
IconManager::~IconManager() {
priv::clear(m_icons);
// release opengl texture is made in ~GLTexture()
}
std::vector<IconManager::Icons> IconManager::init(const InitTypes &input)
{
BOOST_LOG_TRIVIAL(error) << "Not implemented yet";
return {};
}
std::vector<IconManager::Icons> IconManager::init(const std::vector<std::string> &file_paths, const ImVec2 &size, RasterType type)
{
// TODO: remove in future
if (!m_icons.empty()) {
// not first initialization
priv::clear(m_icons);
m_icons.clear();
m_icons_texture.reset();
}
// only rectangle are supported
assert(size.x == size.y);
// no subpixel supported
unsigned int width = static_cast<unsigned int>(std::abs(std::round(size.x)));
assert(size.x == static_cast<float>(width));
// state order has to match the enum IconState
const auto& states = priv::get_states(type);
bool compress = false;
bool is_loaded = m_icons_texture.load_from_svg_files_as_sprites_array(file_paths, states, width, compress);
if (!is_loaded || (size_t) m_icons_texture.get_width() < (states.size() * width) ||
(size_t) m_icons_texture.get_height() < (file_paths.size() * width)) {
// bad load of icons, but all usage of m_icons_texture check that texture is initialized
assert(false);
m_icons_texture.reset();
return {};
}
unsigned count_files = file_paths.size();
// count icons per file
unsigned count = states.size();
// create result
std::vector<Icons> result;
result.reserve(count_files);
Icon def_icon;
def_icon.tex_id = m_icons_texture.get_id();
def_icon.size = size;
// float beacouse of dividing
float tex_height = static_cast<float>(m_icons_texture.get_height());
float tex_width = static_cast<float>(m_icons_texture.get_width());
//for (const auto &f: file_paths) {
for (unsigned f = 0; f < count_files; ++f) {
// NOTE: there are space between icons
unsigned start_y = static_cast<unsigned>(f) * (width + 1) + 1;
float y1 = start_y / tex_height;
float y2 = (start_y + width) / tex_height;
Icons file_icons;
file_icons.reserve(count);
//for (const auto &s : states) {
for (unsigned j = 0; j < count; ++j) {
auto icon = std::make_shared<Icon>(def_icon);
// NOTE: there are space between icons
unsigned start_x = static_cast<unsigned>(j) * (width + 1) + 1;
float x1 = start_x / tex_width;
float x2 = (start_x + width) / tex_width;
icon->tl = ImVec2(x1, y1);
icon->br = ImVec2(x2, y2);
file_icons.push_back(icon);
m_icons.push_back(std::move(icon));
}
result.emplace_back(std::move(file_icons));
}
return result;
}
void IconManager::release() {
BOOST_LOG_TRIVIAL(error) << "Not implemented yet";
}
void priv::clear(IconManager::Icons &icons) {
std::string message;
for (auto &icon : icons) {
// Exist more than this instance of shared ptr?
long count = icon.use_count();
if (count != 1) {
// in existing icon change texture to non existing one
icon->tex_id = 0;
std::string descr =
((count > 2) ? (std::to_string(count - 1) + "x") : "") + // count
std::to_string(icon->size.x) + "x" + std::to_string(icon->size.y); // resolution
if (message.empty())
message = descr;
else
message += ", " + descr;
}
}
if (!message.empty())
BOOST_LOG_TRIVIAL(warning) << "There is still used icons(" << message << ").";
}
const std::vector<std::pair<int, bool>> &priv::get_states(IconManager::RasterType type) {
static std::vector<std::pair<int, bool>> color = {std::make_pair(0, false)};
static std::vector<std::pair<int, bool>> white = {std::make_pair(1, false)};
static std::vector<std::pair<int, bool>> gray = {std::make_pair(2, false)};
static std::vector<std::pair<int, bool>> color_wite_gray = {
std::make_pair(1, false), // Activable
std::make_pair(0, false), // Hovered
std::make_pair(2, false) // Disabled
};
switch (type) {
case IconManager::RasterType::color: return color;
case IconManager::RasterType::white_only_data: return white;
case IconManager::RasterType::gray_only_data: return gray;
case IconManager::RasterType::color_wite_gray: return color_wite_gray;
default: return color;
}
}
void priv::draw_transparent_icon(const IconManager::Icon &icon)
{
// Check input
if (!icon.is_valid()) {
assert(false);
BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon.";
ImGui::Text("?");
return;
}
// size UV texture coors [in texture ratio]
ImVec2 size_uv(icon.br.x - icon.tl.x, icon.br.y - icon.tl.y);
ImVec2 one_px(size_uv.x / icon.size.x, size_uv.y / icon.size.y);
// use top left corner of first icon
IconManager::Icon icon_px = icon; // copy
// reduce uv coors to one pixel
icon_px.tl = ImVec2(0, 0);
icon_px.br = one_px;
draw(icon_px);
}
namespace Slic3r::GUI {
void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col)
{
// Check input
if (!icon.is_valid()) {
assert(false);
BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon.";
ImGui::Text("?");
return;
}
ImTextureID id = (void *)static_cast<intptr_t>(icon.tex_id);
const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size;
ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col);
}
bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover)
{
// check of hover
float cursor_x = ImGui::GetCursorPosX();
priv::draw_transparent_icon(icon);
ImGui::SameLine(cursor_x);
if (ImGui::IsItemHovered()) {
// redraw image
draw(icon_hover);
} else {
// redraw normal image
draw(icon);
}
return ImGui::IsItemClicked();
}
bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled)
{
if (disabled) {
draw(disable);
return false;
}
return clickable(activ, hover);
}
} // namespace Slic3r::GUI

View File

@ -0,0 +1,132 @@
#ifndef slic3r_IconManager_hpp_
#define slic3r_IconManager_hpp_
#include <vector>
#include <memory>
#include "imgui/imgui.h" // ImVec2
#include "slic3r/GUI/GLTexture.hpp" // texture storage
namespace Slic3r::GUI {
/// <summary>
/// Keep texture with icons for UI
/// Manage texture live -> create and destruct texture
/// by live of icon shared pointers.
/// </summary>
class IconManager
{
public:
/// <summary>
/// Release texture
/// Set shared pointers to invalid texture
/// </summary>
~IconManager();
/// <summary>
/// Define way to convert svg data to raster
/// </summary>
enum class RasterType: int{
color = 1 << 1,
white_only_data = 1 << 2,
gray_only_data = 1 << 3,
color_wite_gray = color | white_only_data | gray_only_data
// TODO: add type with backgrounds
};
struct InitType {
// path to file with image .. svg
std::string filepath;
// resolution of stored rasterized icon
ImVec2 size; // float will be rounded
// could contain more than one type
RasterType type = RasterType::color;
// together color, white and gray = color | white_only_data | gray_only_data
};
using InitTypes = std::vector<InitType>;
/// <summary>
/// Data for render texture with icon
/// </summary>
struct Icon {
// stored texture size
ImVec2 size = ImVec2(-1, -1); // [in px] --> unsigned int values stored as float
// SubTexture UV coordinate in range from 0. to 1.
ImVec2 tl; // top left -> uv0
ImVec2 br; // bottom right -> uv1
// OpenGL texture id
unsigned int tex_id = 0;
bool is_valid() const { return tex_id != 0;}
// && size.x > 0 && size.y > 0 && tl.x != br.x && tl.y != br.y;
};
using Icons = std::vector<std::shared_ptr<Icon> >;
// Vector of icons, each vector contain multiple use of a SVG render
using VIcons = std::vector<Icons>;
/// <summary>
/// Initialize raster texture on GPU with given images
/// NOTE: Have to be called after OpenGL initialization
/// </summary>
/// <param name="input">Define files and its </param>
/// <returns>Rasterized icons stored on GPU,
/// Same size and order as input, each item of vector is set of texture in order by RasterType</returns>
VIcons init(const InitTypes &input);
/// <summary>
/// Initialize multiple icons with same settings for size and type
/// NOTE: Have to be called after OpenGL initialization
/// </summary>
/// <param name="file_paths">Define files with icon</param>
/// <param name="size">Size of stored texture[in px], float will be rounded</param>
/// <param name="type">Define way to rasterize icon,
/// together color, white and gray = RasterType::color | RasterType::white_only_data | RasterType::gray_only_data</param>
/// <returns>Rasterized icons stored on GPU,
/// Same size and order as file_paths, each item of vector is set of texture in order by RasterType</returns>
VIcons init(const std::vector<std::string> &file_paths, const ImVec2 &size, RasterType type = RasterType::color);
/// <summary>
/// Release icons which are hold only by this manager
/// May change texture and position of icons.
/// </summary>
void release();
private:
// keep data stored on GPU
GLTexture m_icons_texture;
Icons m_icons;
};
/// <summary>
/// Draw imgui image with icon
/// </summary>
/// <param name="icon">Place in texture</param>
/// <param name="size">[optional]Size of image, wen zero than use same size as stored texture</param>
/// <param name="tint_col">viz ImGui::Image </param>
/// <param name="border_col">viz ImGui::Image </param>
void draw(const IconManager::Icon &icon,
const ImVec2 &size = ImVec2(0, 0),
const ImVec4 &tint_col = ImVec4(1, 1, 1, 1),
const ImVec4 &border_col = ImVec4(0, 0, 0, 0));
/// <summary>
/// Draw icon which change on hover
/// </summary>
/// <param name="icon">Draw when no hover</param>
/// <param name="icon_hover">Draw when hover</param>
/// <returns>True when click, otherwise False</returns>
bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover);
/// <summary>
/// Use icon as button with 3 states activ hover and disabled
/// </summary>
/// <param name="activ">Not disabled not hovered image</param>
/// <param name="hover">Hovered image</param>
/// <param name="disable">Disabled image</param>
/// <returns>True when click on enabled, otherwise False</returns>
bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled = false);
} // namespace Slic3r::GUI
#endif // slic3r_IconManager_hpp_

View File

@ -1369,6 +1369,10 @@ bool ImGuiWrapper::slider_optional_int(const char *label,
} else return false;
}
void ImGuiWrapper::left_inputs() {
ImGui::ClearActiveID();
}
std::string ImGuiWrapper::trunc(const std::string &text,
float width,
const char * tail)
@ -1510,6 +1514,17 @@ void ImGuiWrapper::draw(
}
}
void ImGuiWrapper::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) {
auto draw_list = ImGui::GetOverlayDrawList();
draw_list->AddCircle(position, radius, color, num_segments, thickness);
auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}};
for (const ImVec2 &dir : dirs) {
ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius);
ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius);
draw_list->AddLine(start, end, color, thickness);
}
}
bool ImGuiWrapper::contain_all_glyphs(const ImFont *font,
const std::string &text)
{

View File

@ -146,6 +146,12 @@ public:
// Extended function ImGuiWrapper::slider_float to work with std::optional<int>, when value == def_val than optional release its value
bool slider_optional_int(const char* label, std::optional<int> &v, int v_min, int v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, int def_val = 0);
/// <summary>
/// Use ImGui internals to unactivate (lose focus) in input.
/// When input is activ it can't change value by application.
/// </summary>
static void left_inputs();
/// <summary>
/// Truncate text by ImGui draw function to specific width
/// NOTE 1: ImGui must be initialized
@ -193,6 +199,20 @@ public:
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
float thickness = 3.f);
/// <summary>
/// Draw symbol of cross hair
/// </summary>
/// <param name="position">Center of cross hair</param>
/// <param name="radius">Circle radius</param>
/// <param name="color">Color of symbol</param>
/// <param name="num_segments">Precission of circle</param>
/// <param name="thickness">Thickness of Line in symbol</param>
static void draw_cross_hair(const ImVec2 &position,
float radius = 16.f,
ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)),
int num_segments = 0,
float thickness = 4.f);
/// <summary>
/// Check that font ranges contain all chars in string
/// (rendered Unicodes are stored in GlyphRanges)

View File

@ -2,11 +2,6 @@
// rasterization of ExPoly
#include "libslic3r/SLA/AGGRaster.hpp"
// for get DPI
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
// ability to request new frame after finish rendering
@ -20,15 +15,15 @@ using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
CreateFontStyleImagesJob::CreateFontStyleImagesJob(
StyleManager::StyleImagesData &&input)
: m_input(std::move(input))
CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input)
: m_input(std::move(input)), m_width(0), m_height(0)
{
assert(m_input.result != nullptr);
assert(!m_input.styles.empty());
assert(!m_input.text.empty());
assert(m_input.max_size.x() > 1);
assert(m_input.max_size.y() > 1);
assert(m_input.ppm > 1e-5);
}
void CreateFontStyleImagesJob::process(Ctl &ctl)
@ -36,7 +31,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
// create shapes and calc size (bounding boxes)
std::vector<ExPolygons> name_shapes(m_input.styles.size());
std::vector<double> scales(m_input.styles.size());
images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
m_images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
for (auto &item : m_input.styles) {
size_t index = &item - &m_input.styles.front();
@ -44,21 +39,17 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop);
// create image description
StyleManager::StyleImage &image = images[index];
StyleManager::StyleImage &image = m_images[index];
BoundingBox &bounding_box = image.bounding_box;
for (ExPolygon &shape : shapes)
bounding_box.merge(BoundingBox(shape.contour.points));
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
// calculate conversion from FontPoint to screen pixels by size of font
auto mf = wxGetApp().mainframe;
// dot per inch for monitor
int dpi = get_dpi_for_window(mf);
double ppm = dpi / 25.4; // pixel per milimeter
const auto &cn = item.prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm;
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * m_input.ppm;
scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
@ -78,30 +69,30 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
// arrange bounding boxes
int offset_y = 0;
width = 0;
for (StyleManager::StyleImage &image : images) {
m_width = 0;
for (StyleManager::StyleImage &image : m_images) {
image.offset.y() = offset_y;
offset_y += image.tex_size.y+1;
if (width < image.tex_size.x)
width = image.tex_size.x;
if (m_width < image.tex_size.x)
m_width = image.tex_size.x;
}
height = offset_y;
for (StyleManager::StyleImage &image : images) {
m_height = offset_y;
for (StyleManager::StyleImage &image : m_images) {
const Point &o = image.offset;
const ImVec2 &s = image.tex_size;
image.uv0 = ImVec2(o.x() / (double) width,
o.y() / (double) height);
image.uv1 = ImVec2((o.x() + s.x) / (double) width,
(o.y() + s.y) / (double) height);
image.uv0 = ImVec2(o.x() / (double) m_width,
o.y() / (double) m_height);
image.uv1 = ImVec2((o.x() + s.x) / (double) m_width,
(o.y() + s.y) / (double) m_height);
}
// Set up result
pixels = std::vector<unsigned char>(4*width * height, {255});
m_pixels = std::vector<unsigned char>(4 * m_width * m_height, {255});
// upload sub textures
for (StyleManager::StyleImage &image : images) {
for (StyleManager::StyleImage &image : m_images) {
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
size_t index = &image - &images.front();
size_t index = &image - &m_images.front();
double pixel_dim = SCALING_FACTOR / scales[index];
sla::PixelDim dim(pixel_dim, pixel_dim);
double gamma = 1.;
@ -110,7 +101,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
// copy rastered data to pixels
sla::RasterEncoder encoder = [&offset = image.offset, &pix = pixels, w=width,h=height]
sla::RasterEncoder encoder = [&offset = image.offset, &pix = m_pixels, w=m_width,h=m_height]
(const void *ptr, size_t width, size_t height, size_t num_components) {
// bigger value create darker image
unsigned char gray_level = 5;
@ -142,18 +133,18 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
glsafe(::glBindTexture(target, tex_id));
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
GLint w = width, h=height;
GLint w = m_width, h = m_height;
glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type,
(const void *) pixels.data()));
(const void *) m_pixels.data()));
// set up texture id
void *texture_id = (void *) (intptr_t) tex_id;
for (StyleManager::StyleImage &image : images)
for (StyleManager::StyleImage &image : m_images)
image.texture_id = texture_id;
// move to result
m_input.result->styles = std::move(m_input.styles);
m_input.result->images = std::move(images);
m_input.result->images = std::move(m_images);
// bind default texture
GLuint no_texture_id = 0;

View File

@ -19,11 +19,11 @@ class CreateFontStyleImagesJob : public Job
// Output data
// texture size
int width, height;
int m_width, m_height;
// texture data
std::vector<unsigned char> pixels;
std::vector<unsigned char> m_pixels;
// descriptors of sub textures
std::vector<StyleManager::StyleImage> images;
std::vector<StyleManager::StyleImage> m_images;
public:
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);

View File

@ -502,6 +502,9 @@ void UpdateJob::update_volume(ModelVolume *volume,
volume->calculate_convex_hull();
volume->get_object()->invalidate_bounding_box();
volume->text_configuration = text_configuration;
// discard information about rotation, should not be stored in volume
volume->text_configuration->style.prop.angle.reset();
GUI_App &app = wxGetApp(); // may be move to input
GLCanvas3D *canvas = app.plater()->canvas3D();
@ -615,6 +618,10 @@ void priv::create_volume(
volume->name = data.volume_name; // copy
volume->text_configuration = data.text_configuration; // copy
// discard information about rotation, should not be stored in volume
volume->text_configuration->style.prop.angle.reset();
volume->set_transformation(trmat);
// update printable state on canvas

View File

@ -3410,5 +3410,30 @@ void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& v
}
#endif // ENABLE_WORLD_COORDINATE
ModelVolume *get_selected_volume(const Selection &selection)
{
const GLVolume *gl_volume = get_selected_gl_volume(selection);
if (gl_volume == nullptr)
return nullptr;
const ModelObjectPtrs &objects = selection.get_model()->objects;
return get_model_volume(*gl_volume, objects);
}
const GLVolume *get_selected_gl_volume(const Selection &selection)
{
int object_idx = selection.get_object_idx();
// is more object selected?
if (object_idx == -1)
return nullptr;
const auto &list = selection.get_volume_idxs();
// is more volumes selected?
if (list.size() != 1)
return nullptr;
unsigned int volume_idx = *list.begin();
return selection.get_volume(volume_idx);
}
} // namespace GUI
} // namespace Slic3r

View File

@ -17,6 +17,7 @@ namespace Slic3r {
class Shader;
class Model;
class ModelObject;
class ModelVolume;
class GLVolume;
class GLArrow;
class GLCurvedArrow;
@ -523,6 +524,9 @@ private:
#endif // ENABLE_WORLD_COORDINATE
};
ModelVolume *get_selected_volume(const Selection &selection);
const GLVolume *get_selected_gl_volume(const Selection &selection);
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,348 @@
#include "SurfaceDrag.hpp"
#include "libslic3r/Model.hpp" // ModelVolume
#include "GLCanvas3D.hpp"
#include "slic3r/Utils/RaycastManager.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
#include "libslic3r/Emboss.hpp"
namespace Slic3r::GUI {
/// <summary>
/// Calculate offset from mouse position to center of text
/// </summary>
/// <param name="screen_coor">Position on screen[in Px] e.g. mouse position</param>
/// <param name="volume">Selected volume(text)</param>
/// <param name="camera">Actual position and view direction of camera</param>
/// <returns>Offset in screen coordinate</returns>
static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera)
{
const Transform3d &volume_tr = volume.get_matrix();
assert(volume.text_configuration.has_value());
auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d {
Transform3d to_world = instrance_tr * volume_tr;
// Use fix of .3mf loaded tranformation when exist
if (volume.text_configuration->fix_3mf_tr.has_value())
to_world = to_world * (*volume.text_configuration->fix_3mf_tr);
// zero point of volume in world coordinate system
Vec3d volume_center = to_world.translation();
// screen coordinate of volume center
Vec2i coor = CameraUtils::project(camera, volume_center);
return coor.cast<double>() - screen_coor;
};
auto object = volume.get_object();
assert(!object->instances.empty());
// Speed up for one instance
if (object->instances.size() == 1)
return calc_offset(object->instances.front()->get_matrix());
Vec2d nearest_offset;
double nearest_offset_size = std::numeric_limits<double>::max();
for (const ModelInstance *instance : object->instances) {
Vec2d offset = calc_offset(instance->get_matrix());
double offset_size = offset.norm();
if (nearest_offset_size < offset_size)
continue;
nearest_offset_size = offset_size;
nearest_offset = offset;
}
return nearest_offset;
}
// Calculate scale in world for check in debug
[[maybe_unused]] static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
{
Vec3d from_dir = from * dir;
Vec3d to_dir = to * dir;
double from_scale_sq = from_dir.squaredNorm();
double to_scale_sq = to_dir.squaredNorm();
if (is_approx(from_scale_sq, to_scale_sq, 1e-3))
return {}; // no scale
return sqrt(from_scale_sq / to_scale_sq);
}
bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
const Camera &camera,
std::optional<SurfaceDrag> &surface_drag,
GLCanvas3D &canvas,
RaycastManager &raycast_manager,
std::optional<double> up_limit)
{
// Fix when leave window during dragging
// Fix when click right button
if (surface_drag.has_value() && !mouse_event.Dragging()) {
// write transformation from UI into model
canvas.do_move(L("Surface move"));
// allow moving with object again
canvas.enable_moving(true);
canvas.enable_picking(true);
surface_drag.reset();
// only left up is correct
// otherwise it is fix state and return false
return mouse_event.LeftUp();
}
if (mouse_event.Moving())
return false;
// detect start text dragging
if (mouse_event.LeftDown()) {
// selected volume
GLVolume *gl_volume = get_selected_gl_volume(canvas);
if (gl_volume == nullptr)
return false;
// is selected volume closest hovered?
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
if (int hovered_idx = canvas.get_first_hover_volume_idx();
hovered_idx < 0)
return false;
else if (auto hovered_idx_ = static_cast<size_t>(hovered_idx);
hovered_idx_ >= gl_volumes.size() ||
gl_volumes[hovered_idx_] != gl_volume)
return false;
const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects);
assert(object != nullptr);
if (object == nullptr)
return false;
const ModelInstance *instance = get_model_instance(*gl_volume, *object);
const ModelVolume *volume = get_model_volume(*gl_volume, *object);
assert(instance != nullptr && volume != nullptr);
if (object == nullptr || instance == nullptr || volume == nullptr)
return false;
// allowed drag&drop by canvas for object
if (volume->is_the_only_one_part())
return false;
const ModelVolumePtrs &volumes = object->volumes;
std::vector<size_t> allowed_volumes_id;
if (volumes.size() > 1) {
allowed_volumes_id.reserve(volumes.size() - 1);
for (auto &v : volumes) {
// skip actual selected object
if (v->id() == volume->id())
continue;
// drag only above part not modifiers or negative surface
if (!v->is_model_part())
continue;
allowed_volumes_id.emplace_back(v->id().id);
}
}
RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id));
RaycastManager::Meshes meshes = create_meshes(canvas, condition);
// initialize raycasters
// INFO: It could slows down for big objects
// (may be move to thread and do not show drag until it finish)
raycast_manager.actualize(*instance, &condition, &meshes);
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera);
Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix();
if (volume->text_configuration.has_value()) {
const TextConfiguration &tc = *volume->text_configuration;
// fix baked transformation from .3mf store process
if (tc.fix_3mf_tr.has_value())
volume_tr = volume_tr * tc.fix_3mf_tr->inverse();
}
Transform3d instance_tr = instance->get_matrix();
Transform3d instance_tr_inv = instance_tr.inverse();
Transform3d world_tr = instance_tr * volume_tr;
std::optional<float> start_angle;
if (up_limit.has_value())
start_angle = Emboss::calc_up(world_tr, *up_limit);
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle};
// disable moving with object by mouse
canvas.enable_moving(false);
canvas.enable_picking(false);
return true;
}
// Dragging starts out of window
if (!surface_drag.has_value())
return false;
if (mouse_event.Dragging()) {
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset;
std::optional<RaycastManager::Hit> hit = ray_from_camera(
raycast_manager, offseted_mouse, camera, &surface_drag->condition);
surface_drag->exist_hit = hit.has_value();
if (!hit.has_value()) {
// cross hair need redraw
canvas.set_as_dirty();
return true;
}
auto world_linear = surface_drag->world.linear();
// Calculate offset: transformation to wanted position
{
// Reset skew of the text Z axis:
// Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane.
Vec3d old_z = world_linear.col(2);
Vec3d new_z = world_linear.col(0).cross(world_linear.col(1));
world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm());
}
Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ()
auto z_rotation = Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(text_z_world, hit->normal);
Transform3d world_new = z_rotation * surface_drag->world;
auto world_new_linear = world_new.linear();
// Fix direction of up vector to zero initial rotation
if(up_limit.has_value()){
Vec3d z_world = world_new_linear.col(2);
z_world.normalize();
Vec3d wanted_up = Emboss::suggest_up(z_world, *up_limit);
Vec3d y_world = world_new_linear.col(1);
auto y_rotation = Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(y_world, wanted_up);
world_new = y_rotation * world_new;
world_new_linear = world_new.linear();
}
// Edit position from right
Transform3d volume_new{Eigen::Translation<double, 3>(surface_drag->instance_inv * hit->position)};
volume_new.linear() = surface_drag->instance_inv.linear() * world_new_linear;
// Check that transformation matrix is valid transformation
assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN
if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0))
return true;
// Check that scale in world did not changed
assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value());
assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value());
const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects);
if (volume != nullptr && volume->text_configuration.has_value()) {
const TextConfiguration &tc = *volume->text_configuration;
// fix baked transformation from .3mf store process
if (tc.fix_3mf_tr.has_value())
volume_new = volume_new * (*tc.fix_3mf_tr);
// apply move in Z direction and rotation by up vector
Emboss::apply_transformation(surface_drag->start_angle, tc.style.prop.distance, volume_new);
}
// Update transformation for all instances
for (GLVolume *vol : canvas.get_volumes().volumes) {
if (vol->object_idx() != surface_drag->gl_volume->object_idx() || vol->volume_idx() != surface_drag->gl_volume->volume_idx())
continue;
vol->set_volume_transformation(volume_new);
}
canvas.set_as_dirty();
return true;
}
return false;
}
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager) {
const GLVolume *gl_volume_ptr = get_selected_gl_volume(selection);
if (gl_volume_ptr == nullptr)
return {};
const GLVolume& gl_volume = *gl_volume_ptr;
const ModelObjectPtrs &objects = selection.get_model()->objects;
const ModelVolume* volume = get_model_volume(gl_volume, objects);
if (volume == nullptr)
return {};
const ModelInstance* instance = get_model_instance(gl_volume, objects);
if (instance == nullptr)
return {};
// Move object on surface
auto cond = RaycastManager::SkipVolume(volume->id().id);
raycast_manager.actualize(*instance, &cond);
Transform3d to_world = world_matrix_fixed(gl_volume, selection.get_model()->objects);
Vec3d point = to_world * Vec3d::Zero();
Vec3d direction = to_world.linear() * (-Vec3d::UnitZ());
// ray in direction of text projection(from volume zero to z-dir)
std::optional<RaycastManager::Hit> hit_opt = raycast_manager.closest_hit(point, direction, &cond);
// Try to find closest point when no hit object in emboss direction
if (!hit_opt.has_value()) {
std::optional<RaycastManager::ClosePoint> close_point_opt = raycast_manager.closest(point);
// It should NOT appear. Closest point always exists.
assert(close_point_opt.has_value());
if (!close_point_opt.has_value())
return {};
// It is no neccesary to move with origin by very small value
if (close_point_opt->squared_distance < EPSILON)
return {};
const RaycastManager::ClosePoint &close_point = *close_point_opt;
Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key);
Vec3d hit_world = hit_tr * close_point.point;
Vec3d offset_world = hit_world - point; // vector in world
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
return offset_volume;
}
// It is no neccesary to move with origin by very small value
const RaycastManager::Hit &hit = *hit_opt;
if (hit.squared_distance < EPSILON)
return {};
Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key);
Vec3d hit_world = hit_tr * hit.position;
Vec3d offset_world = hit_world - point; // vector in world
// TIP: It should be close to only z move
Vec3d offset_volume = to_world.inverse().linear() * offset_world;
return offset_volume;
}
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects)
{
Transform3d res = gl_volume.world_matrix();
const ModelVolume *mv = get_model_volume(gl_volume, objects);
if (!mv)
return res;
const std::optional<TextConfiguration> &tc = mv->text_configuration;
if (!tc.has_value())
return res;
const std::optional<Transform3d> &fix = tc->fix_3mf_tr;
if (!fix.has_value())
return res;
return res * fix->inverse();
}
Transform3d world_matrix_fixed(const Selection &selection)
{
const GLVolume *gl_volume = get_selected_gl_volume(selection);
assert(gl_volume != nullptr);
if (gl_volume == nullptr)
return Transform3d::Identity();
return world_matrix_fixed(*gl_volume, selection.get_model()->objects);
}
} // namespace Slic3r::GUI

View File

@ -0,0 +1,90 @@
#ifndef slic3r_SurfaceDrag_hpp_
#define slic3r_SurfaceDrag_hpp_
#include <optional>
#include "libslic3r/Point.hpp" // Vec2d, Transform3d
#include "slic3r/Utils/RaycastManager.hpp"
#include "wx/event.h" // wxMouseEvent
namespace Slic3r {
class GLVolume;
} // namespace Slic3r
namespace Slic3r::GUI {
class GLCanvas3D;
class Selection;
struct Camera;
// Data for drag&drop over surface with mouse
struct SurfaceDrag
{
// hold screen coor offset of cursor from object center
Vec2d mouse_offset;
// Start dragging text transformations to world
Transform3d world;
// Invers transformation of text volume instance
// Help convert world transformation to instance space
Transform3d instance_inv;
// Dragged gl volume
GLVolume *gl_volume;
// condition for raycaster
RaycastManager::AllowVolumes condition;
// initial rotation in Z axis of volume
std::optional<float> start_angle;
// Flag whether coordinate hit some volume
bool exist_hit = true;
};
/// <summary>
/// Mouse event handler, when move(drag&drop) volume over model surface
/// NOTE: Dragged volume has to be selected. And also has to be hovered on start of dragging.
/// </summary>
/// <param name="mouse_event">Contain type of event and mouse position</param>
/// <param name="camera">Actual viewport of camera</param>
/// <param name="surface_drag">Structure which keep information about dragging</param>
/// <param name="canvas">Contain gl_volumes and selection</param>
/// <param name="raycast_manager">AABB trees for raycast in object
/// Refresh state inside of function </param>
/// <param name="up_limit">When set than use correction of up vector</param>
/// <returns>True when event is processed otherwise false</returns>
bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
const Camera &camera,
std::optional<SurfaceDrag> &surface_drag,
GLCanvas3D &canvas,
RaycastManager &raycast_manager,
std::optional<double> up_limit = {});
/// <summary>
/// Calculate translation of volume onto surface of model
/// </summary>
/// <param name="selection">Must contain only one selected volume, Transformation of current instance</param>
/// <param name="raycast_manager">AABB trees of object. Actualize object</param>
/// <returns>Offset of volume in volume coordinate</returns>
std::optional<Vec3d> calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager);
/// <summary>
/// Get transformation to world
/// - use fix after store to 3mf when exists
/// </summary>
/// <param name="gl_volume">Scene volume</param>
/// <param name="objects">To identify Model volume with fix transformation</param>
/// <returns>Fixed Transformation of gl_volume</returns>
Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs& objects);
/// <summary>
/// Get transformation to world
/// - use fix after store to 3mf when exists
/// NOTE: when not one volume selected return identity
/// </summary>
/// <param name="selection">Selected volume</param>
/// <returns>Fixed Transformation of selected volume in selection</returns>
Transform3d world_matrix_fixed(const Selection &selection);
} // namespace Slic3r::GUI
#endif // slic3r_SurfaceDrag_hpp_

View File

@ -228,6 +228,43 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font)
return true;
}
bool StyleManager::is_font_changed() const
{
const wxFont &wx_font = get_wx_font();
if (!wx_font.IsOk())
return false;
if (!exist_stored_style())
return false;
const EmbossStyle *stored_style = get_stored_style();
if (stored_style == nullptr)
return false;
const wxFont &wx_font_stored = get_stored_wx_font();
if (!wx_font_stored.IsOk())
return false;
const FontProp &prop = get_style().prop;
const FontProp &prop_stored = stored_style->prop;
// Exist change in face name?
if(wx_font_stored.GetFaceName() != wx_font.GetFaceName()) return true;
const std::optional<float> &skew = prop.skew;
bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font);
const std::optional<float> &skew_stored = prop_stored.skew;
bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(wx_font_stored);
// is italic changed
if (is_italic != is_stored_italic)
return true;
const std::optional<float> &boldness = prop.boldness;
bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font);
const std::optional<float> &boldness_stored = prop_stored.boldness;
bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(wx_font_stored);
// is bold changed
return is_bold != is_stored_bold;
}
bool StyleManager::is_active_font() { return m_style_cache.font_file.has_value(); }
const EmbossStyle* StyleManager::get_stored_style() const
@ -304,12 +341,15 @@ void StyleManager::init_trunc_names(float max_width) {
}
}
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
// for access to worker
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
// for get DPI
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
void StyleManager::init_style_images(const Vec2i &max_size,
const std::string &text)
{
@ -361,8 +401,15 @@ void StyleManager::init_style_images(const Vec2i &max_size,
style.prop
});
}
auto mf = wxGetApp().mainframe;
// dot per inch for monitor
int dpi = get_dpi_for_window(mf);
// pixel per milimeter
double ppm = dpi / ObjectManipulation::in_to_mm;
auto &worker = wxGetApp().plater()->get_ui_job_worker();
StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images};
StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images, ppm};
queue_job(worker, std::make_unique<CreateFontStyleImagesJob>(std::move(data)));
}
@ -484,7 +531,7 @@ bool StyleManager::set_wx_font(const wxFont &wx_font) {
bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile> font_file)
{
if (font_file == nullptr) return false;
m_style_cache.wx_font = wx_font; // copy
m_style_cache.wx_font = wx_font; // copy
m_style_cache.font_file =
FontFileWithCache(std::move(font_file));

View File

@ -116,15 +116,21 @@ public:
const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; }
const FontProp &get_font_prop() const { return get_style().prop; }
FontProp &get_font_prop() { return get_style().prop; }
const std::optional<wxFont> &get_wx_font() const { return m_style_cache.wx_font; }
const std::optional<wxFont> &get_stored_wx_font() const { return m_style_cache.stored_wx_font; }
const wxFont &get_wx_font() const { return m_style_cache.wx_font; }
const wxFont &get_stored_wx_font() const { return m_style_cache.stored_wx_font; }
Slic3r::Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; }
bool has_collections() const { return m_style_cache.font_file.font_file != nullptr &&
m_style_cache.font_file.font_file->infos.size() > 1; }
// True when activ style has same name as some of stored style
bool exist_stored_style() const { return m_style_cache.style_index != std::numeric_limits<size_t>::max(); }
/// <summary>
/// check whether current style differ to selected
/// </summary>
/// <returns></returns>
bool is_font_changed() const;
/// <summary>
/// Setter on wx_font when changed
/// </summary>
@ -221,7 +227,7 @@ private:
ImFontAtlas atlas = {};
// wx widget font
std::optional<wxFont> wx_font = {};
wxFont wx_font = {};
// cache for view font name with maximal width in imgui
std::string truncated_name;
@ -230,7 +236,7 @@ private:
EmbossStyle style = {};
// cache for stored wx font to not create every frame
std::optional<wxFont> stored_wx_font;
wxFont stored_wx_font = {};
// index into m_style_items
size_t style_index = std::numeric_limits<size_t>::max();
@ -277,6 +283,9 @@ private:
// place to store result in main thread in Finalize
std::shared_ptr<StyleImages> result;
// pixel per milimeter (scaled DPI)
double ppm;
};
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images;
bool m_exist_style_images;

View File

@ -1,54 +1,45 @@
#include "RaycastManager.hpp"
#include <utility>
// include for earn camera
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
using namespace Slic3r::GUI;
using namespace Slic3r::GUI;
void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
namespace{
using namespace Slic3r;
void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr);
const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id);
RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){
return std::make_pair(instance.id().id, volume.id().id); }
RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key);
RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key);
bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) {
return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); }
bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) {
return is_lower_key(i1.first, i2.first); };
template<typename VecType> inline void erase(std::vector<VecType> &vec, const std::vector<bool> &flags);
}
void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes)
{
// check if volume was removed
std::vector<bool> removed_casters(m_raycasters.size(), {true});
// actualize MeshRaycaster
::actualize(m_meshes, object.volumes, skip, meshes);
// check if inscance was removed
std::vector<bool> removed_transf(m_transformations.size(), {true});
// actualize MeshRaycaster
for (const ModelVolume *volume : object->volumes) {
size_t oid = volume->id().id;
if (skip != nullptr && skip->skip(oid))
continue;
auto item = std::find_if(m_raycasters.begin(), m_raycasters.end(),
[oid](const RaycastManager::Raycaster &it)->bool {
return oid == it.first;
});
if (item == m_raycasters.end()) {
// add new raycaster
auto raycaster = std::make_unique<MeshRaycaster>(volume->get_mesh_shared_ptr());
m_raycasters.emplace_back(std::make_pair(oid, std::move(raycaster)));
} else {
size_t index = item - m_raycasters.begin();
removed_casters[index] = false;
}
}
bool need_sort = false;
// actualize transformation matrices
for (const ModelVolume *volume : object->volumes) {
for (const ModelVolume *volume : object.volumes) {
if (skip != nullptr && skip->skip(volume->id().id)) continue;
const Transform3d &volume_tr = volume->get_matrix();
for (const ModelInstance *instance : object->instances) {
const Transform3d &instrance_tr = instance->get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
// TODO: add SLA shift Z
// transformation.translation()(2) += m_sla_shift_z;
TrKey tr_key = std::make_pair(instance->id().id, volume->id().id);
auto item = std::find_if(m_transformations.begin(),
m_transformations.end(),
[&tr_key](const TrItem &it) -> bool {
return it.first == tr_key;
});
for (const ModelInstance *instance : object.instances) {
const Transform3d &instrance_tr = instance->get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
TrKey key = ::create_key(*volume, *instance);
auto item = ::find(m_transformations, key);
if (item != m_transformations.end()) {
// actualize transformation all the time
item->second = transformation;
@ -56,71 +47,128 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
removed_transf[index] = false;
} else {
// add new transformation
m_transformations.emplace_back(
std::make_pair(tr_key, transformation));
m_transformations.emplace_back(key, transformation);
need_sort = true;
}
}
}
// clean other raycasters
for (int i = removed_casters.size() - 1; i >= 0; --i)
if (removed_casters[i])
m_raycasters.erase(m_raycasters.begin() + i);
// clean other transformation
for (int i = removed_transf.size() - 1; i >= 0; --i)
if (removed_transf[i])
m_transformations.erase(m_transformations.begin() + i);
::erase(m_transformations, removed_transf);
if (need_sort)
std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower);
}
std::optional<RaycastManager::Hit> RaycastManager::unproject(
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes)
{
std::optional<Hit> closest;
for (const auto &item : m_transformations) {
const TrKey &key = item.first;
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id)) continue;
const Transform3d &transformation = item.second;
auto raycaster_it =
std::find_if(m_raycasters.begin(), m_raycasters.end(),
[volume_id](const RaycastManager::Raycaster &it)
-> bool { return volume_id == it.first; });
if (raycaster_it == m_raycasters.end()) continue;
const MeshRaycaster &raycaster = *(raycaster_it->second);
SurfacePoint surface_point;
bool success = raycaster.unproject_on_mesh(
mouse_pos, transformation, camera,
surface_point.position, surface_point.normal);
if (!success) continue;
const ModelVolumePtrs &volumes = instance.get_object()->volumes;
Vec3d act_hit_tr = transformation * surface_point.position.cast<double>();
double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm();
if (closest.has_value() &&
closest->squared_distance < squared_distance)
// actualize MeshRaycaster
::actualize(m_meshes, volumes, skip, meshes);
// check if inscance was removed
std::vector<bool> removed_transf(m_transformations.size(), {true});
bool need_sort = false;
// actualize transformation matrices
for (const ModelVolume *volume : volumes) {
if (skip != nullptr && skip->skip(volume->id().id))
continue;
closest = Hit(key, surface_point, squared_distance);
const Transform3d &volume_tr = volume->get_matrix();
const Transform3d &instrance_tr = instance.get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
TrKey key = ::create_key(*volume, instance);
auto item = ::find(m_transformations, key);
if (item != m_transformations.end()) {
// actualize transformation all the time
item->second = transformation;
size_t index = item - m_transformations.begin();
removed_transf[index] = false;
} else {
// add new transformation
m_transformations.emplace_back(key, transformation);
need_sort = true;
}
}
//if (!closest.has_value()) return {};
return closest;
// clean other transformation
::erase(m_transformations, removed_transf);
if (need_sort)
std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower);
}
std::optional<RaycastManager::Hit> RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const
{
// Improve: it is not neccessaru to use AABBMesh and calc normal for every hit
// Results
const AABBMesh *hit_mesh = nullptr;
double hit_squared_distance = 0.;
int hit_face = -1;
Vec3d hit_world;
const Transform3d *hit_tramsformation = nullptr;
const TrKey *hit_key = nullptr;
for (const auto &[key, transformation]: m_transformations) {
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id)) continue;
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
if (mesh == nullptr) continue;
Transform3d inv = transformation.inverse();
// transform input into mesh world
Vec3d point_ = inv * point;
Vec3d direction_= inv.linear() * direction;
std::vector<AABBMesh::hit_result> hits = mesh->query_ray_hits(point_, direction_);
if (hits.empty()) continue; // no intersection found
const AABBMesh::hit_result &hit = hits.front();
// convert to world
Vec3d world = transformation * hit.position();
double squared_distance = (point - world).squaredNorm();
if (hit_mesh != nullptr &&
hit_squared_distance < squared_distance)
continue; // exist closer one
hit_mesh = mesh;
hit_squared_distance = squared_distance;
hit_face = hit.face();
hit_world = world;
hit_tramsformation = &transformation;
hit_key = &key;
}
if (hit_mesh == nullptr)
return {};
// Calculate normal from transformed triangle
// NOTE: Anisotropic transformation of normal is not perpendiculat to triangle
const Vec3i tri = hit_mesh->indices(hit_face);
std::array<Vec3d,3> pts;
auto tr = hit_tramsformation->linear();
for (int i = 0; i < 3; ++i)
pts[i] = tr * hit_mesh->vertices(tri[i]).cast<double>();
Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]);
if (has_reflection(*hit_tramsformation))
normal_world *= -1;
normal_world.normalize();
SurfacePoint<double> point_world{hit_world, normal_world};
return RaycastManager::Hit{point_world, *hit_key, hit_squared_distance};
}
std::optional<RaycastManager::Hit> RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
std::optional<RaycastManager::Hit> RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
{
std::optional<Hit> closest;
for (const auto &item : m_transformations) {
const TrKey &key = item.first;
size_t volume_id = key.second;
for (const auto &[key, transformation] : m_transformations) {
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id)) continue;
const Transform3d &transformation = item.second;
auto raycaster_it =
std::find_if(m_raycasters.begin(), m_raycasters.end(),
[volume_id](const RaycastManager::Raycaster &it)
-> bool { return volume_id == it.first; });
if (raycaster_it == m_raycasters.end()) continue;
const MeshRaycaster &raycaster = *(raycaster_it->second);
const AABBMesh& mesh = raycaster.get_aabb_mesh();
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
if (mesh == nullptr) continue;
Transform3d tr_inv = transformation.inverse();
Vec3d mesh_point = tr_inv * point;
Vec3d mesh_direction = tr_inv.linear() * direction;
@ -130,54 +178,185 @@ std::optional<RaycastManager::Hit> RaycastManager::unproject(const Vec3d &point,
Vec3d point_negative = mesh_point + mesh_direction;
// Throw ray to both directions of ray
std::vector<AABBMesh::hit_result> hits = mesh.query_ray_hits(point_positive, mesh_direction);
std::vector<AABBMesh::hit_result> hits_neg = mesh.query_ray_hits(point_negative, -mesh_direction);
std::vector<AABBMesh::hit_result> hits = mesh->query_ray_hits(point_positive, mesh_direction);
std::vector<AABBMesh::hit_result> hits_neg = mesh->query_ray_hits(point_negative, -mesh_direction);
hits.insert(hits.end(), std::make_move_iterator(hits_neg.begin()), std::make_move_iterator(hits_neg.end()));
for (const AABBMesh::hit_result &hit : hits) {
double squared_distance = (mesh_point - hit.position()).squaredNorm();
if (closest.has_value() &&
closest->squared_distance < squared_distance)
continue;
SurfacePoint surface_point(hit.position().cast<float>(), hit.normal().cast<float>());
closest = Hit(key, surface_point, squared_distance);
closest = Hit{{hit.position(), hit.normal()}, key, squared_distance};
}
}
return closest;
}
std::optional<RaycastManager::Hit> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const {
std::optional<Hit> closest;
for (const auto &item : m_transformations) {
const TrKey &key = item.first;
std::optional<RaycastManager::ClosePoint> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const
{
std::optional<ClosePoint> closest;
for (const auto &[key, transformation] : m_transformations) {
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id))
continue;
auto raycaster_it = std::find_if(m_raycasters.begin(), m_raycasters.end(),
[volume_id](const RaycastManager::Raycaster &it) -> bool { return volume_id == it.first; });
if (raycaster_it == m_raycasters.end())
continue;
const MeshRaycaster &raycaster = *(raycaster_it->second);
const Transform3d &transformation = item.second;
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
if (mesh == nullptr) continue;
Transform3d tr_inv = transformation.inverse();
Vec3d mesh_point_d = tr_inv * point;
Vec3f mesh_point_f = mesh_point_d.cast<float>();
Vec3f n;
Vec3f p = raycaster.get_closest_point(mesh_point_f, &n);
double squared_distance = (mesh_point_f - p).squaredNorm();
Vec3d mesh_point = tr_inv * point;
int face_idx = 0;
Vec3d closest_point;
Vec3d pointd = point.cast<double>();
mesh->squared_distance(pointd, face_idx, closest_point);
double squared_distance = (mesh_point - closest_point).squaredNorm();
if (closest.has_value() && closest->squared_distance < squared_distance)
continue;
SurfacePoint surface_point(p,n);
closest = Hit(key, surface_point, squared_distance);
closest = ClosePoint{key, closest_point, squared_distance};
}
return closest;
}
Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const {
auto item = std::find_if(m_transformations.begin(),
m_transformations.end(),
[&tr_key](const TrItem &it) -> bool {
return it.first == tr_key;
});
if (item == m_transformations.end()) return Transform3d::Identity();
return item->second;
}
auto tr = ::find(m_transformations, tr_key);
if (tr == m_transformations.end())
return Transform3d::Identity();
return tr->second;
}
namespace {
void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs)
{
// check if volume was removed
std::vector<bool> removed_meshes(meshes.size(), {true});
bool need_sort = false;
// actualize MeshRaycaster
for (const ModelVolume *volume : volumes) {
size_t oid = volume->id().id;
if (skip != nullptr && skip->skip(oid))
continue;
auto is_oid = [oid](const RaycastManager::Mesh &it) { return oid == it.first; };
if (auto item = std::find_if(meshes.begin(), meshes.end(), is_oid);
item != meshes.end()) {
size_t index = item - meshes.begin();
removed_meshes[index] = false;
continue;
}
// exist AABB in inputs ?
if (inputs != nullptr) {
auto input = std::find_if(inputs->begin(), inputs->end(), is_oid);
if (input != inputs->end()) {
meshes.emplace_back(std::move(*input));
need_sort = true;
continue;
}
}
// add new raycaster
bool calculate_epsilon = true;
auto mesh = std::make_unique<AABBMesh>(volume->mesh(), calculate_epsilon);
meshes.emplace_back(std::make_pair(oid, std::move(mesh)));
need_sort = true;
}
// clean other raycasters
erase(meshes, removed_meshes);
// All the time meshes must be sorted by volume id - for faster search
if (need_sort) {
auto is_lower = [](const RaycastManager::Mesh &m1, const RaycastManager::Mesh &m2) { return m1.first < m2.first; };
std::sort(meshes.begin(), meshes.end(), is_lower);
}
}
const Slic3r::AABBMesh *get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id)
{
auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) { return m.first < i; };
auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index);
if (it == meshes.end() || it->first != volume_id)
return nullptr;
return &(*(it->second));
}
RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) {
auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); };
auto it = std::lower_bound(items.begin(), items.end(), key, fnc);
if (it != items.end() && it->first != key)
return items.end();
return it;
}
RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key)
{
auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); };
auto it = std::lower_bound(items.begin(), items.end(), key, fnc);
if (it != items.end() && it->first != key)
return items.end();
return it;
}
template<typename VecType> inline void erase(std::vector<VecType> &vec, const std::vector<bool> &flags)
{
if (vec.size() < flags.size() || flags.empty())
return;
// reverse iteration over flags to erase indices from back to front.
for (int i = static_cast<int>(flags.size()) - 1; i >= 0; --i)
if (flags[i])
vec.erase(vec.begin() + i);
}
} // namespace
namespace Slic3r::GUI{
RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition)
{
SceneRaycaster::EType type = SceneRaycaster::EType::Volume;
auto scene_casters = canvas.get_raycasters_for_picking(type);
if (scene_casters == nullptr)
return {};
const std::vector<std::shared_ptr<SceneRaycasterItem>> &casters = *scene_casters;
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
const ModelObjectPtrs &objects = canvas.get_model()->objects;
RaycastManager::Meshes meshes;
for (const std::shared_ptr<SceneRaycasterItem> &caster : casters) {
int index = SceneRaycaster::decode_id(type, caster->get_id());
if (index < 0)
continue;
auto index_ = static_cast<size_t>(index);
if(index_ >= gl_volumes.size())
continue;
const GLVolume *gl_volume = gl_volumes[index_];
if (gl_volume == nullptr)
continue;
const ModelVolume *volume = get_model_volume(*gl_volume, objects);
if (volume == nullptr)
continue;
size_t id = volume->id().id;
if (condition.skip(id))
continue;
auto mesh = std::make_unique<AABBMesh>(caster->get_raycaster()->get_aabb_mesh());
meshes.emplace_back(std::make_pair(id, std::move(mesh)));
}
return meshes;
}
std::optional<RaycastManager::Hit> ray_from_camera(const RaycastManager &raycaster,
const Vec2d &mouse_pos,
const Camera &camera,
const RaycastManager::ISkip *skip)
{
Vec3d point;
Vec3d direction;
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
return raycaster.first_hit(point, direction, skip);
}
} // namespace Slic3r::GUI

View File

@ -2,9 +2,8 @@
#define slic3r_RaycastManager_hpp_
#include <memory> // unique_ptr
#include <optional> // unique_ptr
#include <map>
#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster
#include <optional>
#include "libslic3r/AABBMesh.hpp" // Structure to cast rays
#include "libslic3r/Point.hpp" // Transform3d
#include "libslic3r/ObjectID.hpp"
#include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume
@ -17,19 +16,22 @@ namespace Slic3r::GUI{
/// </summary>
class RaycastManager
{
// ModelVolume.id
using Raycaster = std::pair<size_t, std::unique_ptr<MeshRaycaster> >;
std::vector<Raycaster> m_raycasters;
// Public structures used by RaycastManager
public:
// Key for transformation consist of unique volume and instance
// ModelVolume.id
using Mesh = std::pair<size_t, std::unique_ptr<AABBMesh> >;
using Meshes = std::vector<Mesh>;
// Key for transformation consist of unique volume and instance id ... ObjectId()
// ModelInstance, ModelVolume
using TrKey = std::pair<size_t, size_t>;
using TrItem = std::pair<TrKey, Transform3d>;
std::vector<TrItem> m_transformations;
using TrItems = std::vector<TrItem>;
// should contain shared pointer to camera but it is not shared pointer so it need it every time when casts rays
public:
/// <summary>
/// Interface for identify allowed volumes to cast rays.
/// </summary>
class ISkip{
public:
virtual ~ISkip() = default;
@ -42,6 +44,39 @@ public:
virtual bool skip(const size_t &model_volume_id) const { return false; }
};
// TODO: it is more general object move outside of this class
template<typename T>
struct SurfacePoint {
using Vec3 = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
Vec3 position = Vec3::Zero();
Vec3 normal = Vec3::UnitZ();
};
struct Hit : public SurfacePoint<double>
{
TrKey tr_key;
double squared_distance;
};
struct ClosePoint
{
TrKey tr_key;
Vec3d point;
double squared_distance;
};
// Members
private:
// Keep structure to fast cast rays
// meshes are sorted by volume_id for faster search
Meshes m_meshes;
// Keep transformation of meshes
TrItems m_transformations;
// Note: one mesh could have more transformations ... instances
public:
/// <summary>
/// Actualize raycasters + transformation
/// Detection of removed object
@ -50,28 +85,9 @@ public:
/// </summary>
/// <param name="object">Model representation</param>
/// <param name="skip">Condifiton for skip actualization</param>
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
// TODO: it is more general object move outside of this class
struct SurfacePoint
{
Vec3f position = Vec3f::Zero();
Vec3f normal = Vec3f::UnitZ();
SurfacePoint() = default;
SurfacePoint(Vec3f position, Vec3f normal)
: position(position), normal(normal)
{}
};
struct Hit: public SurfacePoint
{
using Key = TrKey;
Key tr_key;
double squared_distance;
Hit(const Key& tr_key, const SurfacePoint& surface_point, double squared_distance)
: SurfacePoint(surface_point), tr_key(tr_key), squared_distance(squared_distance)
{}
};
/// <param name="meshes">Speed up for already created AABBtrees</param>
void actualize(const ModelObject &object, const ISkip *skip = nullptr, Meshes *meshes = nullptr);
void actualize(const ModelInstance &instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr);
class SkipVolume: public ISkip
{
@ -93,26 +109,24 @@ public:
};
/// <summary>
/// Unproject on mesh by Mesh raycasters
/// Note: Function use current camera position from wxGetApp()
/// Unproject on mesh and return closest hit to point in given direction
/// </summary>
/// <param name="mouse_pos">Position of mouse on screen</param>
/// <param name="camera">Projection params</param>
/// <param name="point">Position in space</param>
/// <param name="direction">Casted ray direction</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction and transformation key, which define hitted object instance</returns>
std::optional<Hit> unproject(const Vec2d &mouse_pos,
const Camera &camera,
const ISkip *skip = nullptr) const;
/// <returns>Position on surface, normal direction in world coorinate
/// + key, to know hitted instance and volume</returns>
std::optional<Hit> first_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
/// <summary>
/// Unproject Ray(point direction) on mesh by MeshRaycasters
/// Unproject Ray(point direction) on mesh to find closest hit of surface in given direction
/// NOTE: It inspect also oposit direction of ray !!
/// </summary>
/// <param name="point">Start point for ray</param>
/// <param name="direction">Direction of ray</param>
/// <param name="direction">Direction of ray, orientation doesn't matter, both are used</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction and transformation key, which define hitted object instance</returns>
std::optional<Hit> unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
std::optional<Hit> closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
/// <summary>
/// Search of closest point
@ -120,16 +134,39 @@ public:
/// <param name="point">Point</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns></returns>
std::optional<Hit> closest(const Vec3d &point, const ISkip *skip = nullptr) const;
std::optional<ClosePoint> closest(const Vec3d &point, const ISkip *skip = nullptr) const;
/// <summary>
/// Getter on transformation
/// Getter on transformation from hitted volume to world
/// </summary>
/// <param name="tr_key">Define transformation</param>
/// <returns>Transformation for key</returns>
Transform3d get_transformation(const TrKey &tr_key) const;
};
class GLCanvas3D;
/// <summary>
/// Use scene Raycasters and prepare data for actualize RaycasterManager
/// </summary>
/// <param name="canvas">contain Scene raycasters</param>
/// <param name="condition">Limit for scene casters</param>
/// <returns>Meshes</returns>
RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition);
struct Camera;
/// <summary>
/// Unproject on mesh by Mesh raycasters
/// </summary>
/// <param name="mouse_pos">Position of mouse on screen</param>
/// <param name="camera">Projection params</param>
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
/// <returns>Position on surface, normal direction in world coorinate
/// + key, to know hitted instance and volume</returns>
std::optional<RaycastManager::Hit> ray_from_camera(const RaycastManager &raycaster,
const Vec2d &mouse_pos,
const Camera &camera,
const RaycastManager::ISkip *skip);
} // namespace Slic3r::GUI
#endif // slic3r_RaycastManager_hpp_

View File

@ -55,13 +55,11 @@ static std::string get_file_path(const wxFont& font) {
if (url == NULL) return {};
wxString file_uri;
wxCFTypeRef(url).GetValue(file_uri);
std::string file_path(wxURI::Unescape(file_uri).c_str());
size_t start = std::string("file://").size();
if (file_path.empty() || file_path.size() <= start)
return {};
// remove prefix file://
file_path = file_path.substr(start, file_path.size() - start);
return file_path;
wxURI uri(file_uri);
const wxString &path = uri.GetPath();
std::string path_str(wxURI::Unescape(path).c_str());
BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ").";
return path_str;
}
#endif // __APPLE__
} // namespace
@ -171,13 +169,23 @@ std::string WxFontUtils::store_wxFont(const wxFont &font)
{
// wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName();
wxString font_descriptor = font.GetNativeFontInfoDesc();
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "' wx string get from GetNativeFontInfoDesc. wxFont " <<
"IsOk(" << font.IsOk() << "), " <<
"isNull(" << font.IsNull() << ")" <<
// "IsFree(" << font.IsFree() << "), " << // on MacOs is no function is free
"IsFixedWidth(" << font.IsFixedWidth() << "), " <<
"IsUsingSizeInPixels(" << font.IsUsingSizeInPixels() << "), " <<
"Encoding(" << (int)font.GetEncoding() << "), " ;
return std::string(font_descriptor.c_str());
}
wxFont WxFontUtils::load_wxFont(const std::string &font_descriptor)
{
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "'font descriptor string param of load_wxFont()";
wxString font_descriptor_wx(font_descriptor);
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor_wx.c_str() << "' wx string descriptor";
wxFont wx_font(font_descriptor_wx);
BOOST_LOG_TRIVIAL(trace) << "loaded font is '" << get_human_readable_name(wx_font) << "'.";
return wx_font;
}

View File

@ -113,6 +113,18 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); }
SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); }
SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); }
SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); }
SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); }
SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); }
SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); }
SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); }
SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); }
SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); }
SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); }
SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); }
SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); }
SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); }