mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-01 10:22:03 +08:00
Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_show_sla_supports
This commit is contained in:
commit
ddf636fe38
@ -1,4 +1,5 @@
|
|||||||
min_slic3r_version = 2.6.0-alpha5
|
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-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%.
|
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
|
min_slic3r_version = 2.6.0-alpha1
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
name = Prusa Research
|
name = Prusa Research
|
||||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
# 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.
|
# 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?
|
# Where to get the updates from?
|
||||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
|
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%
|
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||||
@ -192,7 +192,7 @@ notes =
|
|||||||
overhangs = 1
|
overhangs = 1
|
||||||
only_retract_when_crossing_perimeters = 0
|
only_retract_when_crossing_perimeters = 0
|
||||||
ooze_prevention = 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
|
perimeters = 2
|
||||||
perimeter_extruder = 1
|
perimeter_extruder = 1
|
||||||
perimeter_extrusion_width = 0.45
|
perimeter_extrusion_width = 0.45
|
||||||
@ -390,7 +390,7 @@ support_material_xy_spacing = 80%
|
|||||||
support_material_interface_spacing = 0.2
|
support_material_interface_spacing = 0.2
|
||||||
support_material_spacing = 2.2
|
support_material_spacing = 2.2
|
||||||
raft_first_layer_expansion = 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 = 2
|
||||||
infill_anchor_max = 15
|
infill_anchor_max = 15
|
||||||
thick_bridges = 0
|
thick_bridges = 0
|
||||||
|
@ -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
|
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
|
||||||
// during tree traversal.
|
// during tree traversal.
|
||||||
template<typename LineType>
|
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 CoordType = typename TreeType::CoordType;
|
||||||
using VectorType = typename TreeType::VectorType;
|
using VectorType = typename TreeType::VectorType;
|
||||||
using BoundingBox = typename TreeType::BoundingBox;
|
using BoundingBox = typename TreeType::BoundingBox;
|
||||||
|
@ -1264,15 +1264,17 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emboss::apply_transformation(const FontProp &font_prop,
|
void Emboss::apply_transformation(const FontProp &font_prop, Transform3d &transformation){
|
||||||
Transform3d &transformation)
|
apply_transformation(font_prop.angle, font_prop.distance, transformation);
|
||||||
{
|
}
|
||||||
if (font_prop.angle.has_value()) {
|
|
||||||
double angle_z = *font_prop.angle;
|
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());
|
transformation *= Eigen::AngleAxisd(angle_z, Vec3d::UnitZ());
|
||||||
}
|
}
|
||||||
if (font_prop.distance.has_value()) {
|
if (distance.has_value()) {
|
||||||
Vec3d translate = Vec3d::UnitZ() * (*font_prop.distance);
|
Vec3d translate = Vec3d::UnitZ() * (*distance);
|
||||||
transformation.translate(translate);
|
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);
|
return Vec2d(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
|
|
||||||
const Vec3f &normal,
|
Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit)
|
||||||
float up_limit)
|
|
||||||
{
|
{
|
||||||
// up and emboss direction for generated model
|
// Normal must be 1
|
||||||
Vec3d text_up_dir = Vec3d::UnitY();
|
assert(is_approx(normal.squaredNorm(), 1.));
|
||||||
Vec3d text_emboss_dir = Vec3d::UnitZ();
|
|
||||||
|
|
||||||
// wanted up direction of result
|
// wanted up direction of result
|
||||||
Vec3d wanted_up_side = Vec3d::UnitZ();
|
Vec3d wanted_up_side =
|
||||||
if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY();
|
(std::fabs(normal.z()) > up_limit)?
|
||||||
|
Vec3d::UnitY() : Vec3d::UnitZ();
|
||||||
Vec3d wanted_emboss_dir = normal.cast<double>();
|
|
||||||
// after cast from float it needs to be normalized again
|
|
||||||
wanted_emboss_dir.normalize();
|
|
||||||
|
|
||||||
// create perpendicular unit vector to surface triangle normal vector
|
// create perpendicular unit vector to surface triangle normal vector
|
||||||
// lay on surface of triangle and define up vector for text
|
// lay on surface of triangle and define up vector for text
|
||||||
Vec3d wanted_up_dir = wanted_emboss_dir
|
Vec3d wanted_up_dir = normal.cross(wanted_up_side).cross(normal);
|
||||||
.cross(wanted_up_side)
|
|
||||||
.cross(wanted_emboss_dir);
|
|
||||||
// normal3d is NOT perpendicular to normal_up_dir
|
// 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
|
// perpendicular to emboss vector of text and normal
|
||||||
Vec3d axis_view;
|
Vec3d axis_view;
|
||||||
double angle_view;
|
double angle_view;
|
||||||
if (wanted_emboss_dir == -Vec3d::UnitZ()) {
|
if (normal == -Vec3d::UnitZ()) {
|
||||||
// text_emboss_dir has opposit direction to wanted_emboss_dir
|
// text_emboss_dir has opposit direction to wanted_emboss_dir
|
||||||
axis_view = Vec3d::UnitY();
|
axis_view = Vec3d::UnitY();
|
||||||
angle_view = M_PI;
|
angle_view = M_PI;
|
||||||
} else {
|
} else {
|
||||||
axis_view = text_emboss_dir.cross(wanted_emboss_dir);
|
axis_view = emboss_dir.cross(normal);
|
||||||
angle_view = std::acos(text_emboss_dir.dot(wanted_emboss_dir)); // in rad
|
angle_view = std::acos(emboss_dir.dot(normal)); // in rad
|
||||||
axis_view.normalize();
|
axis_view.normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::AngleAxis view_rot(angle_view, axis_view);
|
Eigen::AngleAxis view_rot(angle_view, axis_view);
|
||||||
Vec3d wanterd_up_rotated = view_rot.matrix().inverse() * wanted_up_dir;
|
Vec3d wanterd_up_rotated = view_rot.matrix().inverse() * wanted_up_dir;
|
||||||
wanterd_up_rotated.normalize();
|
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_view = up_dir.cross(wanterd_up_rotated);
|
||||||
Vec3d text_view2 = text_up_dir.cross(wanterd_up_rotated);
|
Vec3d diff_view = emboss_dir - text_view;
|
||||||
Vec3d diff_view = text_emboss_dir - text_view2;
|
|
||||||
if (std::fabs(diff_view.x()) > 1. ||
|
if (std::fabs(diff_view.x()) > 1. ||
|
||||||
std::fabs(diff_view.y()) > 1. ||
|
std::fabs(diff_view.y()) > 1. ||
|
||||||
std::fabs(diff_view.z()) > 1.) // oposit direction
|
std::fabs(diff_view.z()) > 1.) // oposit direction
|
||||||
angle_up *= -1.;
|
angle_up *= -1.;
|
||||||
|
|
||||||
Eigen::AngleAxis up_rot(angle_up, text_emboss_dir);
|
Eigen::AngleAxis up_rot(angle_up, emboss_dir);
|
||||||
|
|
||||||
Transform3d transform = Transform3d::Identity();
|
Transform3d transform = Transform3d::Identity();
|
||||||
transform.translate(position.cast<double>());
|
transform.translate(position);
|
||||||
transform.rotate(view_rot);
|
transform.rotate(view_rot);
|
||||||
transform.rotate(up_rot);
|
transform.rotate(up_rot);
|
||||||
return transform;
|
return transform;
|
||||||
|
@ -193,6 +193,7 @@ namespace Emboss
|
|||||||
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
|
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
|
||||||
/// <param name="transformation">In / Out transformation to modify by property</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 FontProp &font_prop, Transform3d &transformation);
|
||||||
|
void apply_transformation(const std::optional<float> &angle, const std::optional<float> &distance, Transform3d &transformation);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read information from naming table of font file
|
/// 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>
|
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
|
||||||
/// <returns>Projected shape into space</returns>
|
/// <returns>Projected shape into space</returns>
|
||||||
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
|
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>
|
/// <summary>
|
||||||
/// Create transformation for emboss text object to lay on surface point
|
/// Create transformation for emboss text object to lay on surface point
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -282,7 +299,7 @@ namespace Emboss
|
|||||||
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
||||||
/// <returns>Transformation onto surface point</returns>
|
/// <returns>Transformation onto surface point</returns>
|
||||||
Transform3d create_transformation_onto_surface(
|
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
|
class ProjectZ : public IProjection
|
||||||
{
|
{
|
||||||
|
@ -1397,6 +1397,25 @@ void ModelObject::clone_for_cut(ModelObject** obj)
|
|||||||
(*obj)->input_file.clear();
|
(*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()
|
void ModelVolume::reset_extra_facets()
|
||||||
{
|
{
|
||||||
this->supported_facets.reset();
|
this->supported_facets.reset();
|
||||||
|
@ -835,6 +835,7 @@ public:
|
|||||||
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
|
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_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_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; }
|
t_model_material_id material_id() const { return m_material_id; }
|
||||||
void reset_extra_facets();
|
void reset_extra_facets();
|
||||||
void apply_tolerance();
|
void apply_tolerance();
|
||||||
|
@ -587,7 +587,7 @@ namespace client
|
|||||||
param1.set_s(buf);
|
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;
|
const std::string *subject = nullptr;
|
||||||
if (lhs.type() == TYPE_STRING) {
|
if (lhs.type() == TYPE_STRING) {
|
||||||
@ -601,7 +601,7 @@ namespace client
|
|||||||
bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern));
|
bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern));
|
||||||
if (op == '!')
|
if (op == '!')
|
||||||
result = ! result;
|
result = ! result;
|
||||||
lhs.set_b(result);
|
out.set_b(result);
|
||||||
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) {
|
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) {
|
||||||
// Syntax error in the regular expression
|
// Syntax error in the regular expression
|
||||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
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_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, '!'); }
|
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)
|
static void logical_op(expr &lhs, expr &rhs, char op)
|
||||||
{
|
{
|
||||||
@ -1101,6 +1130,7 @@ namespace client
|
|||||||
{ "multiplicative_expression", "Expecting an expression." },
|
{ "multiplicative_expression", "Expecting an expression." },
|
||||||
{ "unary_expression", "Expecting an expression." },
|
{ "unary_expression", "Expecting an expression." },
|
||||||
{ "optional_parameter", "Expecting a closing brace or an optional parameter." },
|
{ "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."},
|
{ "variable_reference", "Expecting a variable reference."},
|
||||||
{ "is_nil_test", "Expecting a scalar variable reference."},
|
{ "is_nil_test", "Expecting a scalar variable reference."},
|
||||||
{ "variable", "Expecting a variable name."},
|
{ "variable", "Expecting a variable name."},
|
||||||
@ -1221,6 +1251,7 @@ namespace client
|
|||||||
qi::_a_type _a;
|
qi::_a_type _a;
|
||||||
qi::_b_type _b;
|
qi::_b_type _b;
|
||||||
qi::_r1_type _r1;
|
qi::_r1_type _r1;
|
||||||
|
qi::_r2_type _r2;
|
||||||
|
|
||||||
// Starting symbol of the grammer.
|
// Starting symbol of the grammer.
|
||||||
// The leading eps is required by the "expectation point" operator ">".
|
// 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) ]
|
[ px::bind(&expr<Iterator>::template digits<true>, _val, _2, _3) ]
|
||||||
| (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
|
| (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
|
||||||
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _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) ]
|
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
|
||||||
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
||||||
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _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");
|
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)] >> (
|
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
|
||||||
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
||||||
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
||||||
@ -1445,6 +1491,7 @@ namespace client
|
|||||||
("random")
|
("random")
|
||||||
("round")
|
("round")
|
||||||
("not")
|
("not")
|
||||||
|
("one_of")
|
||||||
("or")
|
("or")
|
||||||
("true");
|
("true");
|
||||||
|
|
||||||
@ -1466,6 +1513,8 @@ namespace client
|
|||||||
debug(additive_expression);
|
debug(additive_expression);
|
||||||
debug(multiplicative_expression);
|
debug(multiplicative_expression);
|
||||||
debug(unary_expression);
|
debug(unary_expression);
|
||||||
|
debug(one_of);
|
||||||
|
debug(one_of_list);
|
||||||
debug(optional_parameter);
|
debug(optional_parameter);
|
||||||
debug(variable_reference);
|
debug(variable_reference);
|
||||||
debug(variable);
|
debug(variable);
|
||||||
@ -1517,6 +1566,9 @@ namespace client
|
|||||||
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit_encoding::space_type> variable;
|
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit_encoding::space_type> variable;
|
||||||
// Evaluating whether a nullable variable is nil.
|
// Evaluating whether a nullable variable is nil.
|
||||||
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit_encoding::space_type> is_nil_test;
|
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> ¶m), 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<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;
|
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;
|
||||||
|
@ -49,10 +49,12 @@ struct FontProp
|
|||||||
// used for move over model surface
|
// used for move over model surface
|
||||||
// When not set value is zero and is not stored
|
// When not set value is zero and is not stored
|
||||||
std::optional<float> distance; // [in mm]
|
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
|
// 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
|
// Parameter for True Type Font collections
|
||||||
// Select index of font in collection
|
// Select index of font in collection
|
||||||
|
@ -97,6 +97,8 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
GUI/GUI_Geometry.hpp
|
GUI/GUI_Geometry.hpp
|
||||||
GUI/I18N.cpp
|
GUI/I18N.cpp
|
||||||
GUI/I18N.hpp
|
GUI/I18N.hpp
|
||||||
|
GUI/IconManager.cpp
|
||||||
|
GUI/IconManager.hpp
|
||||||
GUI/MainFrame.cpp
|
GUI/MainFrame.cpp
|
||||||
GUI/MainFrame.hpp
|
GUI/MainFrame.hpp
|
||||||
GUI/Plater.cpp
|
GUI/Plater.cpp
|
||||||
@ -157,6 +159,8 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
GUI/RemovableDriveManager.hpp
|
GUI/RemovableDriveManager.hpp
|
||||||
GUI/SendSystemInfoDialog.cpp
|
GUI/SendSystemInfoDialog.cpp
|
||||||
GUI/SendSystemInfoDialog.hpp
|
GUI/SendSystemInfoDialog.hpp
|
||||||
|
GUI/SurfaceDrag.cpp
|
||||||
|
GUI/SurfaceDrag.hpp
|
||||||
GUI/BonjourDialog.cpp
|
GUI/BonjourDialog.cpp
|
||||||
GUI/BonjourDialog.hpp
|
GUI/BonjourDialog.hpp
|
||||||
GUI/ButtonsDescription.cpp
|
GUI/ButtonsDescription.cpp
|
||||||
|
@ -1407,7 +1407,6 @@ void GLCanvas3D::enable_legend_texture(bool enable)
|
|||||||
void GLCanvas3D::enable_picking(bool enable)
|
void GLCanvas3D::enable_picking(bool enable)
|
||||||
{
|
{
|
||||||
m_picking_enabled = enable;
|
m_picking_enabled = enable;
|
||||||
m_selection.set_mode(Selection::Instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::enable_moving(bool enable)
|
void GLCanvas3D::enable_moving(bool enable)
|
||||||
@ -7335,5 +7334,95 @@ const ModelVolume *get_model_volume(const GLVolume &v, const Model &model)
|
|||||||
return ret;
|
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 GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -1068,7 +1068,20 @@ private:
|
|||||||
float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); }
|
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 GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig
|
|||||||
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
||||||
|
|
||||||
m_canvas->enable_picking(true);
|
m_canvas->enable_picking(true);
|
||||||
|
m_canvas->get_selection().set_mode(Selection::Instance);
|
||||||
m_canvas->enable_moving(true);
|
m_canvas->enable_moving(true);
|
||||||
// XXX: more config from 3D.pm
|
// XXX: more config from 3D.pm
|
||||||
m_canvas->set_model(model);
|
m_canvas->set_model(model);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,15 +5,13 @@
|
|||||||
// which overrides our localization "L" macro.
|
// which overrides our localization "L" macro.
|
||||||
#include "GLGizmoBase.hpp"
|
#include "GLGizmoBase.hpp"
|
||||||
#include "GLGizmoRotate.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/RaycastManager.hpp"
|
||||||
#include "slic3r/Utils/EmbossStyleManager.hpp"
|
#include "slic3r/Utils/EmbossStyleManager.hpp"
|
||||||
|
|
||||||
#include "admesh/stl.h" // indexed_triangle_set
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <thread>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "libslic3r/Emboss.hpp"
|
#include "libslic3r/Emboss.hpp"
|
||||||
@ -28,12 +26,10 @@ class wxFont;
|
|||||||
namespace Slic3r{
|
namespace Slic3r{
|
||||||
class AppConfig;
|
class AppConfig;
|
||||||
class GLVolume;
|
class GLVolume;
|
||||||
|
|
||||||
enum class ModelVolumeType : int;
|
enum class ModelVolumeType : int;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Slic3r::GUI {
|
namespace Slic3r::GUI {
|
||||||
class MeshRaycaster;
|
|
||||||
class GLGizmoEmboss : public GLGizmoBase
|
class GLGizmoEmboss : public GLGizmoBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -82,14 +78,12 @@ protected:
|
|||||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); }
|
std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); }
|
||||||
std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); }
|
std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); }
|
||||||
private:
|
private:
|
||||||
void initialize();
|
|
||||||
static EmbossStyles create_default_styles();
|
static EmbossStyles create_default_styles();
|
||||||
// localized default text
|
// localized default text
|
||||||
void set_default_text();
|
void set_default_text();
|
||||||
|
|
||||||
void set_volume_by_selection();
|
void set_volume_by_selection();
|
||||||
// load text configuration from volume into gizmo
|
void reset_volume();
|
||||||
bool set_volume(ModelVolume *volume);
|
|
||||||
|
|
||||||
// create volume from text - main functionality
|
// create volume from text - main functionality
|
||||||
bool process();
|
bool process();
|
||||||
@ -108,17 +102,21 @@ private:
|
|||||||
void init_font_name_texture();
|
void init_font_name_texture();
|
||||||
struct FaceName;
|
struct FaceName;
|
||||||
void draw_font_preview(FaceName &face, bool is_visible);
|
void draw_font_preview(FaceName &face, bool is_visible);
|
||||||
|
void draw_font_list_line();
|
||||||
void draw_font_list();
|
void draw_font_list();
|
||||||
void draw_style_edit();
|
|
||||||
void draw_height(bool use_inch);
|
void draw_height(bool use_inch);
|
||||||
void draw_depth(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_italic_button();
|
||||||
bool draw_bold_button();
|
bool draw_bold_button();
|
||||||
void draw_advanced();
|
void draw_advanced();
|
||||||
|
|
||||||
bool select_facename(const wxString& facename);
|
bool select_facename(const wxString& facename);
|
||||||
void init_face_names();
|
|
||||||
|
|
||||||
void do_translate(const Vec3d& relative_move);
|
void do_translate(const Vec3d& relative_move);
|
||||||
void do_rotate(float relative_z_angle);
|
void do_rotate(float relative_z_angle);
|
||||||
@ -145,7 +143,7 @@ private:
|
|||||||
template<typename T, typename Draw>
|
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 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);
|
void set_minimal_window_size(bool is_advance_edit_style);
|
||||||
ImVec2 get_minimal_window_size() const;
|
ImVec2 get_minimal_window_size() const;
|
||||||
|
|
||||||
@ -153,41 +151,43 @@ private:
|
|||||||
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
|
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
|
||||||
bool on_mouse_for_translate(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
|
// When open text loaded from .3mf it could be written with unknown font
|
||||||
bool m_is_unknown_font;
|
bool m_is_unknown_font;
|
||||||
void create_notification_not_valid_font(const TextConfiguration& tc);
|
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();
|
void remove_notification_not_valid_font();
|
||||||
|
|
||||||
// This configs holds GUI layout size given by translated texts.
|
// This configs holds GUI layout size given by translated texts.
|
||||||
// etc. When language changes, GUI is recreated and this class constructed again,
|
// etc. When language changes, GUI is recreated and this class constructed again,
|
||||||
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
||||||
struct GuiCfg
|
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
|
// Zero means it is calculated in init function
|
||||||
ImVec2 minimal_window_size = ImVec2(0, 0);
|
ImVec2 minimal_window_size = ImVec2(0, 0);
|
||||||
ImVec2 minimal_window_size_with_advance = ImVec2(0, 0);
|
ImVec2 minimal_window_size_with_advance = ImVec2(0, 0);
|
||||||
ImVec2 minimal_window_size_with_collections = ImVec2(0, 0);
|
ImVec2 minimal_window_size_with_collections = ImVec2(0, 0);
|
||||||
float height_of_volume_type_selector = 0.f;
|
float height_of_volume_type_selector = 0.f;
|
||||||
float input_width = 0.f;
|
float input_width = 0.f;
|
||||||
float delete_pos_x = 0.f;
|
float delete_pos_x = 0.f;
|
||||||
float max_style_name_width = 0.f;
|
float max_style_name_width = 0.f;
|
||||||
unsigned int icon_width = 0;
|
unsigned int icon_width = 0;
|
||||||
|
|
||||||
// maximal width and height of style image
|
// maximal width and height of style image
|
||||||
Vec2i max_style_image_size = Vec2i(0, 0);
|
Vec2i max_style_image_size = Vec2i(0, 0);
|
||||||
|
|
||||||
|
float indent = 0.f;
|
||||||
float input_offset = 0.f;
|
float input_offset = 0.f;
|
||||||
float advanced_input_offset = 0.f;
|
float advanced_input_offset = 0.f;
|
||||||
|
|
||||||
ImVec2 text_size;
|
ImVec2 text_size;
|
||||||
|
|
||||||
// maximal size of face name image
|
// maximal size of face name image
|
||||||
Vec2i face_name_size = Vec2i(100, 0);
|
Vec2i face_name_size = Vec2i(100, 0);
|
||||||
float face_name_max_width = 100.f;
|
float face_name_max_width = 100.f;
|
||||||
float face_name_texture_offset_x = 105.f;
|
float face_name_texture_offset_x = 105.f;
|
||||||
|
|
||||||
// maximal texture generate jobs running at once
|
// maximal texture generate jobs running at once
|
||||||
@ -197,7 +197,7 @@ private:
|
|||||||
struct Translations
|
struct Translations
|
||||||
{
|
{
|
||||||
std::string font;
|
std::string font;
|
||||||
std::string size;
|
std::string height;
|
||||||
std::string depth;
|
std::string depth;
|
||||||
std::string use_surface;
|
std::string use_surface;
|
||||||
|
|
||||||
@ -205,16 +205,18 @@ private:
|
|||||||
std::string char_gap;
|
std::string char_gap;
|
||||||
std::string line_gap;
|
std::string line_gap;
|
||||||
std::string boldness;
|
std::string boldness;
|
||||||
std::string italic;
|
std::string skew_ration;
|
||||||
std::string surface_distance;
|
std::string from_surface;
|
||||||
std::string angle;
|
std::string rotation;
|
||||||
|
std::string keep_up;
|
||||||
std::string collection;
|
std::string collection;
|
||||||
};
|
};
|
||||||
Translations translations;
|
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;
|
bool m_is_advanced_edit_style = false;
|
||||||
|
|
||||||
// when true window will appear near to text volume when open
|
// 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
|
// setted only when wanted to use - not all the time
|
||||||
std::optional<ImVec2> m_set_window_offset;
|
std::optional<ImVec2> m_set_window_offset;
|
||||||
|
|
||||||
|
// Keep information about stored styles and loaded actual style to compare with
|
||||||
Emboss::StyleManager m_style_manager;
|
Emboss::StyleManager m_style_manager;
|
||||||
|
|
||||||
struct FaceName{
|
struct FaceName{
|
||||||
@ -244,13 +247,15 @@ private:
|
|||||||
// true .. already enumerated(During opened combo box)
|
// true .. already enumerated(During opened combo box)
|
||||||
bool is_init = false;
|
bool is_init = false;
|
||||||
|
|
||||||
|
bool has_truncated_names = false;
|
||||||
|
|
||||||
// data of can_load() faces
|
// data of can_load() faces
|
||||||
std::vector<FaceName> faces = {};
|
std::vector<FaceName> faces = {};
|
||||||
// Sorter set of Non valid face names in OS
|
// Sorter set of Non valid face names in OS
|
||||||
std::vector<wxString> bad = {};
|
std::vector<wxString> bad = {};
|
||||||
|
|
||||||
// Configuration of font encoding
|
// Configuration of font encoding
|
||||||
const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
|
static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
|
||||||
|
|
||||||
// Identify if preview texture exists
|
// Identify if preview texture exists
|
||||||
GLuint texture_id = 0;
|
GLuint texture_id = 0;
|
||||||
@ -277,11 +282,17 @@ private:
|
|||||||
static bool store(const Facenames &facenames);
|
static bool store(const Facenames &facenames);
|
||||||
static bool load(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
|
// 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;
|
ModelVolume *m_volume;
|
||||||
|
|
||||||
// When work with undo redo stack there could be situation that
|
// 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
|
// Value is set only when dragging rotation to calculate actual angle
|
||||||
std::optional<float> m_rotate_start_angle;
|
std::optional<float> m_rotate_start_angle;
|
||||||
|
|
||||||
// when draging with text object hold screen offset of cursor from object center
|
// Keep data about dragging only during drag&drop
|
||||||
std::optional<Vec2d> m_dragging_mouse_offset;
|
std::optional<SurfaceDrag> m_surface_drag;
|
||||||
|
|
||||||
// TODO: it should be accessible by other gizmo too.
|
// TODO: it should be accessible by other gizmo too.
|
||||||
// May be move to plater?
|
// May be move to plater?
|
||||||
RaycastManager m_raycast_manager;
|
RaycastManager m_raycast_manager;
|
||||||
|
|
||||||
// Only when drag text object it stores world position
|
|
||||||
std::optional<Transform3d> m_temp_transformation;
|
|
||||||
|
|
||||||
// For text on scaled objects
|
// For text on scaled objects
|
||||||
std::optional<float> m_scale_height;
|
std::optional<float> m_scale_height;
|
||||||
std::optional<float> m_scale_depth;
|
std::optional<float> m_scale_depth;
|
||||||
void calculate_scale();
|
void calculate_scale();
|
||||||
|
|
||||||
// drawing icons
|
// drawing icons
|
||||||
GLTexture m_icons_texture;
|
IconManager m_icon_manager;
|
||||||
|
IconManager::VIcons m_icons;
|
||||||
void init_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
|
// only temporary solution
|
||||||
static const std::string M_ICON_FILENAME;
|
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
|
} // namespace Slic3r::GUI
|
||||||
|
204
src/slic3r/GUI/IconManager.cpp
Normal file
204
src/slic3r/GUI/IconManager.cpp
Normal 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
|
132
src/slic3r/GUI/IconManager.hpp
Normal file
132
src/slic3r/GUI/IconManager.hpp
Normal 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_
|
@ -1369,6 +1369,10 @@ bool ImGuiWrapper::slider_optional_int(const char *label,
|
|||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImGuiWrapper::left_inputs() {
|
||||||
|
ImGui::ClearActiveID();
|
||||||
|
}
|
||||||
|
|
||||||
std::string ImGuiWrapper::trunc(const std::string &text,
|
std::string ImGuiWrapper::trunc(const std::string &text,
|
||||||
float width,
|
float width,
|
||||||
const char * tail)
|
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,
|
bool ImGuiWrapper::contain_all_glyphs(const ImFont *font,
|
||||||
const std::string &text)
|
const std::string &text)
|
||||||
{
|
{
|
||||||
|
@ -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
|
// 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);
|
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>
|
/// <summary>
|
||||||
/// Truncate text by ImGui draw function to specific width
|
/// Truncate text by ImGui draw function to specific width
|
||||||
/// NOTE 1: ImGui must be initialized
|
/// NOTE 1: ImGui must be initialized
|
||||||
@ -193,6 +199,20 @@ public:
|
|||||||
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
|
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
|
||||||
float thickness = 3.f);
|
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>
|
/// <summary>
|
||||||
/// Check that font ranges contain all chars in string
|
/// Check that font ranges contain all chars in string
|
||||||
/// (rendered Unicodes are stored in GlyphRanges)
|
/// (rendered Unicodes are stored in GlyphRanges)
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
// rasterization of ExPoly
|
// rasterization of ExPoly
|
||||||
#include "libslic3r/SLA/AGGRaster.hpp"
|
#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
|
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||||
|
|
||||||
// ability to request new frame after finish rendering
|
// ability to request new frame after finish rendering
|
||||||
@ -20,15 +15,15 @@ using namespace Slic3r::GUI;
|
|||||||
using namespace Slic3r::GUI::Emboss;
|
using namespace Slic3r::GUI::Emboss;
|
||||||
|
|
||||||
|
|
||||||
CreateFontStyleImagesJob::CreateFontStyleImagesJob(
|
CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input)
|
||||||
StyleManager::StyleImagesData &&input)
|
: m_input(std::move(input)), m_width(0), m_height(0)
|
||||||
: m_input(std::move(input))
|
|
||||||
{
|
{
|
||||||
assert(m_input.result != nullptr);
|
assert(m_input.result != nullptr);
|
||||||
assert(!m_input.styles.empty());
|
assert(!m_input.styles.empty());
|
||||||
assert(!m_input.text.empty());
|
assert(!m_input.text.empty());
|
||||||
assert(m_input.max_size.x() > 1);
|
assert(m_input.max_size.x() > 1);
|
||||||
assert(m_input.max_size.y() > 1);
|
assert(m_input.max_size.y() > 1);
|
||||||
|
assert(m_input.ppm > 1e-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateFontStyleImagesJob::process(Ctl &ctl)
|
void CreateFontStyleImagesJob::process(Ctl &ctl)
|
||||||
@ -36,7 +31,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
// create shapes and calc size (bounding boxes)
|
// create shapes and calc size (bounding boxes)
|
||||||
std::vector<ExPolygons> name_shapes(m_input.styles.size());
|
std::vector<ExPolygons> name_shapes(m_input.styles.size());
|
||||||
std::vector<double> scales(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) {
|
for (auto &item : m_input.styles) {
|
||||||
size_t index = &item - &m_input.styles.front();
|
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);
|
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop);
|
||||||
|
|
||||||
// create image description
|
// create image description
|
||||||
StyleManager::StyleImage &image = images[index];
|
StyleManager::StyleImage &image = m_images[index];
|
||||||
BoundingBox &bounding_box = image.bounding_box;
|
BoundingBox &bounding_box = image.bounding_box;
|
||||||
for (ExPolygon &shape : shapes)
|
for (ExPolygon &shape : shapes)
|
||||||
bounding_box.merge(BoundingBox(shape.contour.points));
|
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||||
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||||
|
|
||||||
// calculate conversion from FontPoint to screen pixels by size of font
|
// 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;
|
const auto &cn = item.prop.collection_number;
|
||||||
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
||||||
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
|
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;
|
scales[index] = scale;
|
||||||
|
|
||||||
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
||||||
@ -78,30 +69,30 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
|
|
||||||
// arrange bounding boxes
|
// arrange bounding boxes
|
||||||
int offset_y = 0;
|
int offset_y = 0;
|
||||||
width = 0;
|
m_width = 0;
|
||||||
for (StyleManager::StyleImage &image : images) {
|
for (StyleManager::StyleImage &image : m_images) {
|
||||||
image.offset.y() = offset_y;
|
image.offset.y() = offset_y;
|
||||||
offset_y += image.tex_size.y+1;
|
offset_y += image.tex_size.y+1;
|
||||||
if (width < image.tex_size.x)
|
if (m_width < image.tex_size.x)
|
||||||
width = image.tex_size.x;
|
m_width = image.tex_size.x;
|
||||||
}
|
}
|
||||||
height = offset_y;
|
m_height = offset_y;
|
||||||
for (StyleManager::StyleImage &image : images) {
|
for (StyleManager::StyleImage &image : m_images) {
|
||||||
const Point &o = image.offset;
|
const Point &o = image.offset;
|
||||||
const ImVec2 &s = image.tex_size;
|
const ImVec2 &s = image.tex_size;
|
||||||
image.uv0 = ImVec2(o.x() / (double) width,
|
image.uv0 = ImVec2(o.x() / (double) m_width,
|
||||||
o.y() / (double) height);
|
o.y() / (double) m_height);
|
||||||
image.uv1 = ImVec2((o.x() + s.x) / (double) width,
|
image.uv1 = ImVec2((o.x() + s.x) / (double) m_width,
|
||||||
(o.y() + s.y) / (double) height);
|
(o.y() + s.y) / (double) m_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up result
|
// 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
|
// 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);
|
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];
|
double pixel_dim = SCALING_FACTOR / scales[index];
|
||||||
sla::PixelDim dim(pixel_dim, pixel_dim);
|
sla::PixelDim dim(pixel_dim, pixel_dim);
|
||||||
double gamma = 1.;
|
double gamma = 1.;
|
||||||
@ -110,7 +101,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||||||
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
|
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
|
||||||
|
|
||||||
// copy rastered data to pixels
|
// 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) {
|
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
||||||
// bigger value create darker image
|
// bigger value create darker image
|
||||||
unsigned char gray_level = 5;
|
unsigned char gray_level = 5;
|
||||||
@ -142,18 +133,18 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
|
|||||||
glsafe(::glBindTexture(target, tex_id));
|
glsafe(::glBindTexture(target, tex_id));
|
||||||
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||||
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_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,
|
glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type,
|
||||||
(const void *) pixels.data()));
|
(const void *) m_pixels.data()));
|
||||||
|
|
||||||
// set up texture id
|
// set up texture id
|
||||||
void *texture_id = (void *) (intptr_t) tex_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;
|
image.texture_id = texture_id;
|
||||||
|
|
||||||
// move to result
|
// move to result
|
||||||
m_input.result->styles = std::move(m_input.styles);
|
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
|
// bind default texture
|
||||||
GLuint no_texture_id = 0;
|
GLuint no_texture_id = 0;
|
||||||
|
@ -19,11 +19,11 @@ class CreateFontStyleImagesJob : public Job
|
|||||||
|
|
||||||
// Output data
|
// Output data
|
||||||
// texture size
|
// texture size
|
||||||
int width, height;
|
int m_width, m_height;
|
||||||
// texture data
|
// texture data
|
||||||
std::vector<unsigned char> pixels;
|
std::vector<unsigned char> m_pixels;
|
||||||
// descriptors of sub textures
|
// descriptors of sub textures
|
||||||
std::vector<StyleManager::StyleImage> images;
|
std::vector<StyleManager::StyleImage> m_images;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
|
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
|
||||||
|
@ -502,6 +502,9 @@ void UpdateJob::update_volume(ModelVolume *volume,
|
|||||||
volume->calculate_convex_hull();
|
volume->calculate_convex_hull();
|
||||||
volume->get_object()->invalidate_bounding_box();
|
volume->get_object()->invalidate_bounding_box();
|
||||||
volume->text_configuration = text_configuration;
|
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
|
GUI_App &app = wxGetApp(); // may be move to input
|
||||||
GLCanvas3D *canvas = app.plater()->canvas3D();
|
GLCanvas3D *canvas = app.plater()->canvas3D();
|
||||||
@ -615,6 +618,10 @@ void priv::create_volume(
|
|||||||
|
|
||||||
volume->name = data.volume_name; // copy
|
volume->name = data.volume_name; // copy
|
||||||
volume->text_configuration = data.text_configuration; // 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);
|
volume->set_transformation(trmat);
|
||||||
|
|
||||||
// update printable state on canvas
|
// update printable state on canvas
|
||||||
|
@ -3410,5 +3410,30 @@ void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& v
|
|||||||
}
|
}
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#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 GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -17,6 +17,7 @@ namespace Slic3r {
|
|||||||
class Shader;
|
class Shader;
|
||||||
class Model;
|
class Model;
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
|
class ModelVolume;
|
||||||
class GLVolume;
|
class GLVolume;
|
||||||
class GLArrow;
|
class GLArrow;
|
||||||
class GLCurvedArrow;
|
class GLCurvedArrow;
|
||||||
@ -523,6 +524,9 @@ private:
|
|||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ModelVolume *get_selected_volume(const Selection &selection);
|
||||||
|
const GLVolume *get_selected_gl_volume(const Selection &selection);
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
348
src/slic3r/GUI/SurfaceDrag.cpp
Normal file
348
src/slic3r/GUI/SurfaceDrag.cpp
Normal 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
|
90
src/slic3r/GUI/SurfaceDrag.hpp
Normal file
90
src/slic3r/GUI/SurfaceDrag.hpp
Normal 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_
|
@ -228,6 +228,43 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font)
|
|||||||
return true;
|
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(); }
|
bool StyleManager::is_active_font() { return m_style_cache.font_file.has_value(); }
|
||||||
|
|
||||||
const EmbossStyle* StyleManager::get_stored_style() const
|
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
|
// for access to worker
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/Plater.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,
|
void StyleManager::init_style_images(const Vec2i &max_size,
|
||||||
const std::string &text)
|
const std::string &text)
|
||||||
{
|
{
|
||||||
@ -361,8 +401,15 @@ void StyleManager::init_style_images(const Vec2i &max_size,
|
|||||||
style.prop
|
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();
|
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)));
|
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)
|
bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile> font_file)
|
||||||
{
|
{
|
||||||
if (font_file == nullptr) return false;
|
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 =
|
m_style_cache.font_file =
|
||||||
FontFileWithCache(std::move(font_file));
|
FontFileWithCache(std::move(font_file));
|
||||||
|
|
||||||
|
@ -116,15 +116,21 @@ public:
|
|||||||
const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; }
|
const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; }
|
||||||
const FontProp &get_font_prop() const { return get_style().prop; }
|
const FontProp &get_font_prop() const { return get_style().prop; }
|
||||||
FontProp &get_font_prop() { 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 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_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; }
|
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 &&
|
bool has_collections() const { return m_style_cache.font_file.font_file != nullptr &&
|
||||||
m_style_cache.font_file.font_file->infos.size() > 1; }
|
m_style_cache.font_file.font_file->infos.size() > 1; }
|
||||||
|
|
||||||
// True when activ style has same name as some of stored style
|
// 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(); }
|
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>
|
/// <summary>
|
||||||
/// Setter on wx_font when changed
|
/// Setter on wx_font when changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -221,7 +227,7 @@ private:
|
|||||||
ImFontAtlas atlas = {};
|
ImFontAtlas atlas = {};
|
||||||
|
|
||||||
// wx widget font
|
// wx widget font
|
||||||
std::optional<wxFont> wx_font = {};
|
wxFont wx_font = {};
|
||||||
|
|
||||||
// cache for view font name with maximal width in imgui
|
// cache for view font name with maximal width in imgui
|
||||||
std::string truncated_name;
|
std::string truncated_name;
|
||||||
@ -230,7 +236,7 @@ private:
|
|||||||
EmbossStyle style = {};
|
EmbossStyle style = {};
|
||||||
|
|
||||||
// cache for stored wx font to not create every frame
|
// 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
|
// index into m_style_items
|
||||||
size_t style_index = std::numeric_limits<size_t>::max();
|
size_t style_index = std::numeric_limits<size_t>::max();
|
||||||
@ -277,6 +283,9 @@ private:
|
|||||||
|
|
||||||
// place to store result in main thread in Finalize
|
// place to store result in main thread in Finalize
|
||||||
std::shared_ptr<StyleImages> result;
|
std::shared_ptr<StyleImages> result;
|
||||||
|
|
||||||
|
// pixel per milimeter (scaled DPI)
|
||||||
|
double ppm;
|
||||||
};
|
};
|
||||||
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images;
|
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images;
|
||||||
bool m_exist_style_images;
|
bool m_exist_style_images;
|
||||||
|
@ -1,54 +1,45 @@
|
|||||||
#include "RaycastManager.hpp"
|
#include "RaycastManager.hpp"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
// include for earn camera
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
|
||||||
#include "slic3r/GUI/Camera.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
|
// actualize MeshRaycaster
|
||||||
std::vector<bool> removed_casters(m_raycasters.size(), {true});
|
::actualize(m_meshes, object.volumes, skip, meshes);
|
||||||
|
|
||||||
// check if inscance was removed
|
// check if inscance was removed
|
||||||
std::vector<bool> removed_transf(m_transformations.size(), {true});
|
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
|
// actualize transformation matrices
|
||||||
for (const ModelVolume *volume : object->volumes) {
|
for (const ModelVolume *volume : object.volumes) {
|
||||||
if (skip != nullptr && skip->skip(volume->id().id)) continue;
|
if (skip != nullptr && skip->skip(volume->id().id)) continue;
|
||||||
const Transform3d &volume_tr = volume->get_matrix();
|
const Transform3d &volume_tr = volume->get_matrix();
|
||||||
for (const ModelInstance *instance : object->instances) {
|
for (const ModelInstance *instance : object.instances) {
|
||||||
const Transform3d &instrance_tr = instance->get_matrix();
|
const Transform3d &instrance_tr = instance->get_matrix();
|
||||||
Transform3d transformation = instrance_tr * volume_tr;
|
Transform3d transformation = instrance_tr * volume_tr;
|
||||||
// TODO: add SLA shift Z
|
TrKey key = ::create_key(*volume, *instance);
|
||||||
// transformation.translation()(2) += m_sla_shift_z;
|
auto item = ::find(m_transformations, key);
|
||||||
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;
|
|
||||||
});
|
|
||||||
if (item != m_transformations.end()) {
|
if (item != m_transformations.end()) {
|
||||||
// actualize transformation all the time
|
// actualize transformation all the time
|
||||||
item->second = transformation;
|
item->second = transformation;
|
||||||
@ -56,71 +47,128 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip)
|
|||||||
removed_transf[index] = false;
|
removed_transf[index] = false;
|
||||||
} else {
|
} else {
|
||||||
// add new transformation
|
// add new transformation
|
||||||
m_transformations.emplace_back(
|
m_transformations.emplace_back(key, transformation);
|
||||||
std::make_pair(tr_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
|
// clean other transformation
|
||||||
for (int i = removed_transf.size() - 1; i >= 0; --i)
|
::erase(m_transformations, removed_transf);
|
||||||
if (removed_transf[i])
|
|
||||||
m_transformations.erase(m_transformations.begin() + i);
|
if (need_sort)
|
||||||
|
std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<RaycastManager::Hit> RaycastManager::unproject(
|
void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes)
|
||||||
const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const
|
|
||||||
{
|
{
|
||||||
std::optional<Hit> closest;
|
const ModelVolumePtrs &volumes = instance.get_object()->volumes;
|
||||||
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;
|
|
||||||
|
|
||||||
Vec3d act_hit_tr = transformation * surface_point.position.cast<double>();
|
// actualize MeshRaycaster
|
||||||
double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm();
|
::actualize(m_meshes, volumes, skip, meshes);
|
||||||
if (closest.has_value() &&
|
|
||||||
closest->squared_distance < squared_distance)
|
// 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;
|
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 {};
|
// clean other transformation
|
||||||
return closest;
|
::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;
|
std::optional<Hit> closest;
|
||||||
for (const auto &item : m_transformations) {
|
for (const auto &[key, transformation] : m_transformations) {
|
||||||
const TrKey &key = item.first;
|
size_t volume_id = key.second;
|
||||||
size_t volume_id = key.second;
|
|
||||||
if (skip != nullptr && skip->skip(volume_id)) continue;
|
if (skip != nullptr && skip->skip(volume_id)) continue;
|
||||||
const Transform3d &transformation = item.second;
|
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
|
||||||
auto raycaster_it =
|
if (mesh == nullptr) continue;
|
||||||
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();
|
|
||||||
Transform3d tr_inv = transformation.inverse();
|
Transform3d tr_inv = transformation.inverse();
|
||||||
Vec3d mesh_point = tr_inv * point;
|
Vec3d mesh_point = tr_inv * point;
|
||||||
Vec3d mesh_direction = tr_inv.linear() * direction;
|
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;
|
Vec3d point_negative = mesh_point + mesh_direction;
|
||||||
|
|
||||||
// Throw ray to both directions of ray
|
// 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 = 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_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()));
|
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) {
|
for (const AABBMesh::hit_result &hit : hits) {
|
||||||
double squared_distance = (mesh_point - hit.position()).squaredNorm();
|
double squared_distance = (mesh_point - hit.position()).squaredNorm();
|
||||||
if (closest.has_value() &&
|
if (closest.has_value() &&
|
||||||
closest->squared_distance < squared_distance)
|
closest->squared_distance < squared_distance)
|
||||||
continue;
|
continue;
|
||||||
SurfacePoint surface_point(hit.position().cast<float>(), hit.normal().cast<float>());
|
closest = Hit{{hit.position(), hit.normal()}, key, squared_distance};
|
||||||
closest = Hit(key, surface_point, squared_distance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<RaycastManager::Hit> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const {
|
std::optional<RaycastManager::ClosePoint> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const
|
||||||
std::optional<Hit> closest;
|
{
|
||||||
for (const auto &item : m_transformations) {
|
std::optional<ClosePoint> closest;
|
||||||
const TrKey &key = item.first;
|
for (const auto &[key, transformation] : m_transformations) {
|
||||||
size_t volume_id = key.second;
|
size_t volume_id = key.second;
|
||||||
if (skip != nullptr && skip->skip(volume_id))
|
if (skip != nullptr && skip->skip(volume_id))
|
||||||
continue;
|
continue;
|
||||||
auto raycaster_it = std::find_if(m_raycasters.begin(), m_raycasters.end(),
|
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
|
||||||
[volume_id](const RaycastManager::Raycaster &it) -> bool { return volume_id == it.first; });
|
if (mesh == nullptr) continue;
|
||||||
if (raycaster_it == m_raycasters.end())
|
|
||||||
continue;
|
|
||||||
const MeshRaycaster &raycaster = *(raycaster_it->second);
|
|
||||||
const Transform3d &transformation = item.second;
|
|
||||||
Transform3d tr_inv = transformation.inverse();
|
Transform3d tr_inv = transformation.inverse();
|
||||||
Vec3d mesh_point_d = tr_inv * point;
|
Vec3d mesh_point = tr_inv * point;
|
||||||
Vec3f mesh_point_f = mesh_point_d.cast<float>();
|
|
||||||
Vec3f n;
|
int face_idx = 0;
|
||||||
Vec3f p = raycaster.get_closest_point(mesh_point_f, &n);
|
Vec3d closest_point;
|
||||||
double squared_distance = (mesh_point_f - p).squaredNorm();
|
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)
|
if (closest.has_value() && closest->squared_distance < squared_distance)
|
||||||
continue;
|
continue;
|
||||||
SurfacePoint surface_point(p,n);
|
|
||||||
closest = Hit(key, surface_point, squared_distance);
|
closest = ClosePoint{key, closest_point, squared_distance};
|
||||||
}
|
}
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const {
|
Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const {
|
||||||
auto item = std::find_if(m_transformations.begin(),
|
auto tr = ::find(m_transformations, tr_key);
|
||||||
m_transformations.end(),
|
if (tr == m_transformations.end())
|
||||||
[&tr_key](const TrItem &it) -> bool {
|
return Transform3d::Identity();
|
||||||
return it.first == tr_key;
|
return tr->second;
|
||||||
});
|
}
|
||||||
if (item == m_transformations.end()) return Transform3d::Identity();
|
|
||||||
return item->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
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
#define slic3r_RaycastManager_hpp_
|
#define slic3r_RaycastManager_hpp_
|
||||||
|
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <optional> // unique_ptr
|
#include <optional>
|
||||||
#include <map>
|
#include "libslic3r/AABBMesh.hpp" // Structure to cast rays
|
||||||
#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster
|
|
||||||
#include "libslic3r/Point.hpp" // Transform3d
|
#include "libslic3r/Point.hpp" // Transform3d
|
||||||
#include "libslic3r/ObjectID.hpp"
|
#include "libslic3r/ObjectID.hpp"
|
||||||
#include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume
|
#include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume
|
||||||
@ -17,19 +16,22 @@ namespace Slic3r::GUI{
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class RaycastManager
|
class RaycastManager
|
||||||
{
|
{
|
||||||
// ModelVolume.id
|
// Public structures used by RaycastManager
|
||||||
using Raycaster = std::pair<size_t, std::unique_ptr<MeshRaycaster> >;
|
public:
|
||||||
std::vector<Raycaster> m_raycasters;
|
|
||||||
|
|
||||||
// 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
|
// ModelInstance, ModelVolume
|
||||||
using TrKey = std::pair<size_t, size_t>;
|
using TrKey = std::pair<size_t, size_t>;
|
||||||
using TrItem = std::pair<TrKey, Transform3d>;
|
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
|
/// <summary>
|
||||||
|
/// Interface for identify allowed volumes to cast rays.
|
||||||
public:
|
/// </summary>
|
||||||
class ISkip{
|
class ISkip{
|
||||||
public:
|
public:
|
||||||
virtual ~ISkip() = default;
|
virtual ~ISkip() = default;
|
||||||
@ -42,6 +44,39 @@ public:
|
|||||||
virtual bool skip(const size_t &model_volume_id) const { return false; }
|
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>
|
/// <summary>
|
||||||
/// Actualize raycasters + transformation
|
/// Actualize raycasters + transformation
|
||||||
/// Detection of removed object
|
/// Detection of removed object
|
||||||
@ -50,28 +85,9 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="object">Model representation</param>
|
/// <param name="object">Model representation</param>
|
||||||
/// <param name="skip">Condifiton for skip actualization</param>
|
/// <param name="skip">Condifiton for skip actualization</param>
|
||||||
void actualize(const ModelObject *object, const ISkip *skip = nullptr);
|
/// <param name="meshes">Speed up for already created AABBtrees</param>
|
||||||
|
void actualize(const ModelObject &object, const ISkip *skip = nullptr, Meshes *meshes = nullptr);
|
||||||
// TODO: it is more general object move outside of this class
|
void actualize(const ModelInstance &instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr);
|
||||||
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)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SkipVolume: public ISkip
|
class SkipVolume: public ISkip
|
||||||
{
|
{
|
||||||
@ -93,26 +109,24 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unproject on mesh by Mesh raycasters
|
/// Unproject on mesh and return closest hit to point in given direction
|
||||||
/// Note: Function use current camera position from wxGetApp()
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouse_pos">Position of mouse on screen</param>
|
/// <param name="point">Position in space</param>
|
||||||
/// <param name="camera">Projection params</param>
|
/// <param name="direction">Casted ray direction</param>
|
||||||
/// <param name="skip">Define which caster will be skipped, null mean no skip</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>
|
/// <returns>Position on surface, normal direction in world coorinate
|
||||||
std::optional<Hit> unproject(const Vec2d &mouse_pos,
|
/// + key, to know hitted instance and volume</returns>
|
||||||
const Camera &camera,
|
std::optional<Hit> first_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const;
|
||||||
const ISkip *skip = nullptr) const;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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 !!
|
/// NOTE: It inspect also oposit direction of ray !!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="point">Start point for ray</param>
|
/// <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>
|
/// <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>
|
/// <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>
|
/// <summary>
|
||||||
/// Search of closest point
|
/// Search of closest point
|
||||||
@ -120,16 +134,39 @@ public:
|
|||||||
/// <param name="point">Point</param>
|
/// <param name="point">Point</param>
|
||||||
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
|
/// <param name="skip">Define which caster will be skipped, null mean no skip</param>
|
||||||
/// <returns></returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Getter on transformation
|
/// Getter on transformation from hitted volume to world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tr_key">Define transformation</param>
|
/// <param name="tr_key">Define transformation</param>
|
||||||
/// <returns>Transformation for key</returns>
|
/// <returns>Transformation for key</returns>
|
||||||
Transform3d get_transformation(const TrKey &tr_key) const;
|
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
|
} // namespace Slic3r::GUI
|
||||||
|
|
||||||
#endif // slic3r_RaycastManager_hpp_
|
#endif // slic3r_RaycastManager_hpp_
|
||||||
|
@ -55,13 +55,11 @@ static std::string get_file_path(const wxFont& font) {
|
|||||||
if (url == NULL) return {};
|
if (url == NULL) return {};
|
||||||
wxString file_uri;
|
wxString file_uri;
|
||||||
wxCFTypeRef(url).GetValue(file_uri);
|
wxCFTypeRef(url).GetValue(file_uri);
|
||||||
std::string file_path(wxURI::Unescape(file_uri).c_str());
|
wxURI uri(file_uri);
|
||||||
size_t start = std::string("file://").size();
|
const wxString &path = uri.GetPath();
|
||||||
if (file_path.empty() || file_path.size() <= start)
|
std::string path_str(wxURI::Unescape(path).c_str());
|
||||||
return {};
|
BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ").";
|
||||||
// remove prefix file://
|
return path_str;
|
||||||
file_path = file_path.substr(start, file_path.size() - start);
|
|
||||||
return file_path;
|
|
||||||
}
|
}
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -171,13 +169,23 @@ std::string WxFontUtils::store_wxFont(const wxFont &font)
|
|||||||
{
|
{
|
||||||
// wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName();
|
// wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName();
|
||||||
wxString font_descriptor = font.GetNativeFontInfoDesc();
|
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());
|
return std::string(font_descriptor.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxFont WxFontUtils::load_wxFont(const std::string &font_descriptor)
|
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);
|
wxString font_descriptor_wx(font_descriptor);
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor_wx.c_str() << "' wx string descriptor";
|
||||||
wxFont wx_font(font_descriptor_wx);
|
wxFont wx_font(font_descriptor_wx);
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "loaded font is '" << get_human_readable_name(wx_font) << "'.";
|
||||||
return wx_font;
|
return wx_font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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: 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: 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: 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 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 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)")); }
|
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)")); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user