mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge branch 'dev_native' of https://github.com/prusa3d/Slic3r into dev_native
This commit is contained in:
		
						commit
						7e71aaffd5
					
				
					 36 changed files with 1313 additions and 565 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
#include "libslic3r.h"
 | 
			
		||||
#include "Geometry.hpp"
 | 
			
		||||
#include "ClipperUtils.hpp"
 | 
			
		||||
#include "ExPolygon.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -369,12 +370,6 @@ contains(const std::vector<T> &vector, const Point &point)
 | 
			
		|||
}
 | 
			
		||||
template bool contains(const ExPolygons &vector, const Point &point);
 | 
			
		||||
 | 
			
		||||
double
 | 
			
		||||
rad2deg(double angle)
 | 
			
		||||
{
 | 
			
		||||
    return angle / PI * 180.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double
 | 
			
		||||
rad2deg_dir(double angle)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1232,4 +1227,161 @@ Vec3d extract_euler_angles(const Transform3d& transform)
 | 
			
		|||
    return extract_euler_angles(m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
Transformation::Flags::Flags()
 | 
			
		||||
    : dont_translate(true)
 | 
			
		||||
    , dont_rotate(true)
 | 
			
		||||
    , dont_scale(true)
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    , dont_mirror(true)
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
 | 
			
		||||
{
 | 
			
		||||
    return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::Flags::set(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror)
 | 
			
		||||
{
 | 
			
		||||
    this->dont_translate = dont_translate;
 | 
			
		||||
    this->dont_rotate = dont_rotate;
 | 
			
		||||
    this->dont_scale = dont_scale;
 | 
			
		||||
    this->dont_mirror = dont_mirror;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale) const
 | 
			
		||||
{
 | 
			
		||||
    return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::Flags::set(bool dont_translate, bool dont_rotate, bool dont_scale)
 | 
			
		||||
{
 | 
			
		||||
    this->dont_translate = dont_translate;
 | 
			
		||||
    this->dont_rotate = dont_rotate;
 | 
			
		||||
    this->dont_scale = dont_scale;
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
 | 
			
		||||
Transformation::Transformation()
 | 
			
		||||
    : m_offset(Vec3d::Zero())
 | 
			
		||||
    , m_rotation(Vec3d::Zero())
 | 
			
		||||
    , m_scaling_factor(Vec3d::Ones())
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    , m_mirror(Vec3d::Ones())
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
    , m_matrix(Transform3d::Identity())
 | 
			
		||||
    , m_dirty(false)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_offset(const Vec3d& offset)
 | 
			
		||||
{
 | 
			
		||||
    set_offset(X, offset(0));
 | 
			
		||||
    set_offset(Y, offset(1));
 | 
			
		||||
    set_offset(Z, offset(2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_offset(Axis axis, double offset)
 | 
			
		||||
{
 | 
			
		||||
    if (m_offset(axis) != offset)
 | 
			
		||||
    {
 | 
			
		||||
        m_offset(axis) = offset;
 | 
			
		||||
        m_dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_rotation(const Vec3d& rotation)
 | 
			
		||||
{
 | 
			
		||||
    set_rotation(X, rotation(0));
 | 
			
		||||
    set_rotation(Y, rotation(1));
 | 
			
		||||
    set_rotation(Z, rotation(2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_rotation(Axis axis, double rotation)
 | 
			
		||||
{
 | 
			
		||||
    rotation = angle_to_0_2PI(rotation);
 | 
			
		||||
 | 
			
		||||
    if (m_rotation(axis) = rotation)
 | 
			
		||||
    {
 | 
			
		||||
        m_rotation(axis) = rotation;
 | 
			
		||||
        m_dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_scaling_factor(const Vec3d& scaling_factor)
 | 
			
		||||
{
 | 
			
		||||
    set_scaling_factor(X, scaling_factor(0));
 | 
			
		||||
    set_scaling_factor(Y, scaling_factor(1));
 | 
			
		||||
    set_scaling_factor(Z, scaling_factor(2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_scaling_factor(Axis axis, double scaling_factor)
 | 
			
		||||
{
 | 
			
		||||
    if (m_scaling_factor(axis) != std::abs(scaling_factor))
 | 
			
		||||
    {
 | 
			
		||||
        m_scaling_factor(axis) = std::abs(scaling_factor);
 | 
			
		||||
        m_dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_mirror(const Vec3d& mirror)
 | 
			
		||||
{
 | 
			
		||||
    set_mirror(X, mirror(0));
 | 
			
		||||
    set_mirror(Y, mirror(1));
 | 
			
		||||
    set_mirror(Z, mirror(2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transformation::set_mirror(Axis axis, double mirror)
 | 
			
		||||
{
 | 
			
		||||
    double abs_mirror = std::abs(mirror);
 | 
			
		||||
    if (abs_mirror == 0.0)
 | 
			
		||||
        mirror = 1.0;
 | 
			
		||||
    else if (abs_mirror != 1.0)
 | 
			
		||||
        mirror /= abs_mirror;
 | 
			
		||||
 | 
			
		||||
    if (m_mirror(axis) != mirror)
 | 
			
		||||
    {
 | 
			
		||||
        m_mirror(axis) = mirror;
 | 
			
		||||
        m_dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
const Transform3d& Transformation::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
 | 
			
		||||
#else
 | 
			
		||||
const Transform3d& Transformation::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale) const
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
{
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror))
 | 
			
		||||
#else
 | 
			
		||||
    if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale))
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
    {
 | 
			
		||||
        Vec3d translation = dont_translate ? Vec3d::Zero() : m_offset;
 | 
			
		||||
        Vec3d rotation = dont_rotate ? Vec3d::Zero() : m_rotation;
 | 
			
		||||
        Vec3d scale = dont_scale ? Vec3d::Ones() : m_scaling_factor;
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
        Vec3d mirror = dont_mirror ? Vec3d::Ones() : m_mirror;
 | 
			
		||||
        m_matrix = Geometry::assemble_transform(translation, rotation, scale, mirror);
 | 
			
		||||
#else
 | 
			
		||||
        m_matrix = Geometry::assemble_transform(translation, rotation, scale);
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
        m_flags.set(dont_translate, dont_rotate, dont_scale, dont_mirror);
 | 
			
		||||
#else
 | 
			
		||||
        m_flags.set(dont_translate, dont_rotate, dont_scale);
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
        m_dirty = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return m_matrix;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
} }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,9 +117,23 @@ void chained_path(const Points &points, std::vector<Points::size_type> &retval);
 | 
			
		|||
template<class T> void chained_path_items(Points &points, T &items, T &retval);
 | 
			
		||||
bool directions_parallel(double angle1, double angle2, double max_diff = 0);
 | 
			
		||||
template<class T> bool contains(const std::vector<T> &vector, const Point &point);
 | 
			
		||||
double rad2deg(double angle);
 | 
			
		||||
template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
 | 
			
		||||
double rad2deg_dir(double angle);
 | 
			
		||||
template<typename T> T deg2rad(T angle) { return T(PI) * angle / T(180.0); }
 | 
			
		||||
template<typename T> T angle_to_0_2PI(T angle)
 | 
			
		||||
{
 | 
			
		||||
    static const T TWO_PI = T(2) * T(PI);
 | 
			
		||||
    while (angle < T(0))
 | 
			
		||||
    {
 | 
			
		||||
        angle += TWO_PI;
 | 
			
		||||
    }
 | 
			
		||||
    while (TWO_PI < angle)
 | 
			
		||||
    {
 | 
			
		||||
        angle -= TWO_PI;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return angle;
 | 
			
		||||
}
 | 
			
		||||
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval);
 | 
			
		||||
 | 
			
		||||
double linint(double value, double oldmin, double oldmax, double newmin, double newmax);
 | 
			
		||||
| 
						 | 
				
			
			@ -200,6 +214,79 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>&
 | 
			
		|||
// Returns the euler angles extracted from the given affine transform
 | 
			
		||||
// Warning -> The transform should not contain any shear !!!
 | 
			
		||||
Vec3d extract_euler_angles(const Transform3d& transform);
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
class Transformation
 | 
			
		||||
{
 | 
			
		||||
    struct Flags
 | 
			
		||||
    {
 | 
			
		||||
        bool dont_translate;
 | 
			
		||||
        bool dont_rotate;
 | 
			
		||||
        bool dont_scale;
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
        bool dont_mirror;
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
 | 
			
		||||
        Flags();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
        bool needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const;
 | 
			
		||||
        void set(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror);
 | 
			
		||||
#else
 | 
			
		||||
        bool needs_update(bool dont_translate, bool dont_rotate, bool dont_scale) const;
 | 
			
		||||
        void set(bool dont_translate, bool dont_rotate, bool dont_scale);
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Vec3d m_offset;              // In unscaled coordinates
 | 
			
		||||
    Vec3d m_rotation;            // Rotation around the three axes, in radians around mesh center point
 | 
			
		||||
    Vec3d m_scaling_factor;      // Scaling factors along the three axes
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    Vec3d m_mirror;              // Mirroring along the three axes
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
    mutable Transform3d m_matrix;
 | 
			
		||||
 | 
			
		||||
    mutable Flags m_flags;
 | 
			
		||||
    mutable bool m_dirty;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    Transformation();
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_offset() const { return m_offset; }
 | 
			
		||||
    double get_offset(Axis axis) const { return m_offset(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_offset(const Vec3d& offset);
 | 
			
		||||
    void set_offset(Axis axis, double offset);
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_rotation() const { return m_rotation; }
 | 
			
		||||
    double get_rotation(Axis axis) const { return m_rotation(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_rotation(const Vec3d& rotation);
 | 
			
		||||
    void set_rotation(Axis axis, double rotation);
 | 
			
		||||
 | 
			
		||||
    Vec3d get_scaling_factor() const { return m_scaling_factor; }
 | 
			
		||||
    double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    void set_scaling_factor(const Vec3d& scaling_factor);
 | 
			
		||||
    void set_scaling_factor(Axis axis, double scaling_factor);
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_mirror() const { return m_mirror; }
 | 
			
		||||
    double get_mirror(Axis axis) const { return m_mirror(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_mirror(const Vec3d& mirror);
 | 
			
		||||
    void set_mirror(Axis axis, double mirror);
 | 
			
		||||
 | 
			
		||||
    const Transform3d& world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;
 | 
			
		||||
#else
 | 
			
		||||
    void set_scaling_factor(const Vec3d& scaling_factor) { m_scaling_factor = scaling_factor; }
 | 
			
		||||
    void set_scaling_factor(Axis axis, double scaling_factor) { m_scaling_factor(axis) = scaling_factor; }
 | 
			
		||||
 | 
			
		||||
    const Transform3d& world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const;
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
};
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
} }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -712,18 +712,32 @@ void ModelObject::center_around_origin()
 | 
			
		|||
 | 
			
		||||
    if (!this->instances.empty()) {
 | 
			
		||||
        for (ModelInstance *i : this->instances) {
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
            i->set_offset(i->get_offset() - shift);
 | 
			
		||||
#else
 | 
			
		||||
            // apply rotation and scaling to vector as well before translating instance,
 | 
			
		||||
            // in order to leave final position unaltered
 | 
			
		||||
            i->set_offset(i->get_offset() + i->transform_vector(-shift, true));
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
        }
 | 
			
		||||
        this->invalidate_bounding_box();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModelObject::ensure_on_bed()
 | 
			
		||||
{
 | 
			
		||||
    translate_instances(Vec3d(0.0, 0.0, -get_min_z()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModelObject::translate_instances(const Vec3d& vector)
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < instances.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        translate_instance(i, vector);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
 | 
			
		||||
{
 | 
			
		||||
    ModelInstance* i = instances[instance_idx];
 | 
			
		||||
    i->set_offset(i->get_offset() + vector);
 | 
			
		||||
    invalidate_bounding_box();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
 | 
			
		||||
{
 | 
			
		||||
    for (ModelVolume *v : this->volumes)
 | 
			
		||||
| 
						 | 
				
			
			@ -895,6 +909,42 @@ void ModelObject::repair()
 | 
			
		|||
        v->mesh.repair();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double ModelObject::get_min_z() const
 | 
			
		||||
{
 | 
			
		||||
    if (instances.empty())
 | 
			
		||||
        return 0.0;
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        double min_z = DBL_MAX;
 | 
			
		||||
        for (size_t i = 0; i < instances.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            min_z = std::min(min_z, get_instance_min_z(i));
 | 
			
		||||
        }
 | 
			
		||||
        return min_z;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double ModelObject::get_instance_min_z(size_t instance_idx) const
 | 
			
		||||
{
 | 
			
		||||
    double min_z = DBL_MAX;
 | 
			
		||||
 | 
			
		||||
    ModelInstance* inst = instances[instance_idx];
 | 
			
		||||
    const Transform3d& m = inst->world_matrix(true);
 | 
			
		||||
 | 
			
		||||
    for (ModelVolume *v : volumes)
 | 
			
		||||
    {
 | 
			
		||||
        for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f)
 | 
			
		||||
        {
 | 
			
		||||
            const stl_facet* facet = v->mesh.stl.facet_start + f;
 | 
			
		||||
            min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[0].cast<double>()));
 | 
			
		||||
            min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[1].cast<double>()));
 | 
			
		||||
            min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[2].cast<double>()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return min_z + inst->get_offset(Z);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
 | 
			
		||||
{
 | 
			
		||||
    unsigned int num_printable = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1074,6 +1124,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
 | 
			
		|||
    return idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
void ModelInstance::set_rotation(const Vec3d& rotation)
 | 
			
		||||
{
 | 
			
		||||
    set_rotation(X, rotation(0));
 | 
			
		||||
| 
						 | 
				
			
			@ -1126,6 +1177,7 @@ void ModelInstance::set_mirror(Axis axis, double mirror)
 | 
			
		|||
    m_mirror(axis) = mirror;
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1136,24 +1188,40 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
 | 
			
		|||
{
 | 
			
		||||
    // Rotate around mesh origin.
 | 
			
		||||
    TriangleMesh copy(*mesh);
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    copy.transform(world_matrix(true, false, true, true).cast<float>());
 | 
			
		||||
#else
 | 
			
		||||
    copy.transform(world_matrix(true, false, true).cast<float>());
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
    BoundingBoxf3 bbox = copy.bounding_box();
 | 
			
		||||
 | 
			
		||||
    if (!empty(bbox)) {
 | 
			
		||||
        // Scale the bounding box along the three axes.
 | 
			
		||||
        for (unsigned int i = 0; i < 3; ++i)
 | 
			
		||||
        {
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
            if (std::abs(m_transformation.get_scaling_factor((Axis)i)-1.0) > EPSILON)
 | 
			
		||||
            {
 | 
			
		||||
                bbox.min(i) *= m_transformation.get_scaling_factor((Axis)i);
 | 
			
		||||
                bbox.max(i) *= m_transformation.get_scaling_factor((Axis)i);
 | 
			
		||||
#else
 | 
			
		||||
            if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON)
 | 
			
		||||
            {
 | 
			
		||||
                bbox.min(i) *= this->m_scaling_factor(i);
 | 
			
		||||
                bbox.max(i) *= this->m_scaling_factor(i);
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Translate the bounding box.
 | 
			
		||||
        if (! dont_translate) {
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
            bbox.min += m_transformation.get_offset();
 | 
			
		||||
            bbox.max += m_transformation.get_offset();
 | 
			
		||||
#else
 | 
			
		||||
            bbox.min += this->m_offset;
 | 
			
		||||
            bbox.max += this->m_offset;
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return bbox;
 | 
			
		||||
| 
						 | 
				
			
			@ -1171,12 +1239,20 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const
 | 
			
		|||
 | 
			
		||||
void ModelInstance::transform_polygon(Polygon* polygon) const
 | 
			
		||||
{
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
 | 
			
		||||
    polygon->rotate(m_transformation.get_rotation(Z)); // rotate around polygon origin
 | 
			
		||||
    // CHECK_ME -> Is the following correct ?
 | 
			
		||||
    polygon->scale(m_transformation.get_scaling_factor(X), m_transformation.get_scaling_factor(Y)); // scale around polygon origin
 | 
			
		||||
#else
 | 
			
		||||
    // CHECK_ME -> Is the following correct or it should take in account all three rotations ?
 | 
			
		||||
    polygon->rotate(this->m_rotation(2));                // rotate around polygon origin
 | 
			
		||||
    // CHECK_ME -> Is the following correct ?
 | 
			
		||||
    polygon->scale(this->m_scaling_factor(0), this->m_scaling_factor(1));           // scale around polygon origin
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -1193,5 +1269,6 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b
 | 
			
		|||
    return Geometry::assemble_transform(translation, rotation, scale);
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
}
 | 
			
		||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,9 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
#include "Geometry.hpp"
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +156,9 @@ public:
 | 
			
		|||
    // A snug bounding box around the transformed non-modifier object volumes.
 | 
			
		||||
    BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
 | 
			
		||||
    void center_around_origin();
 | 
			
		||||
    void ensure_on_bed();
 | 
			
		||||
    void translate_instances(const Vec3d& vector);
 | 
			
		||||
    void translate_instance(size_t instance_idx, const Vec3d& vector);
 | 
			
		||||
    void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
 | 
			
		||||
    void translate(coordf_t x, coordf_t y, coordf_t z);
 | 
			
		||||
    void scale(const Vec3d &versor);
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +173,9 @@ public:
 | 
			
		|||
    void split(ModelObjectPtrs* new_objects);
 | 
			
		||||
    void repair();
 | 
			
		||||
 | 
			
		||||
    double get_min_z() const;
 | 
			
		||||
    double get_instance_min_z(size_t instance_idx) const;
 | 
			
		||||
 | 
			
		||||
    // Called by Print::validate() from the UI thread.
 | 
			
		||||
    unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +213,10 @@ class ModelVolume : public ModelBase
 | 
			
		|||
    // The convex hull of this model's mesh.
 | 
			
		||||
    TriangleMesh        m_convex_hull;
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    Geometry::Transformation m_transformation;
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    std::string         name;
 | 
			
		||||
    // The triangular model.
 | 
			
		||||
| 
						 | 
				
			
			@ -251,6 +264,34 @@ public:
 | 
			
		|||
    static Type         type_from_string(const std::string &s);
 | 
			
		||||
    static std::string  type_to_string(const Type t);
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    const Vec3d& get_offset() const { return m_transformation.get_offset(); }
 | 
			
		||||
    double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); }
 | 
			
		||||
    void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); }
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_rotation() const { return m_transformation.get_rotation(); }
 | 
			
		||||
    double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
 | 
			
		||||
    void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
 | 
			
		||||
 | 
			
		||||
    Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
 | 
			
		||||
    double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
 | 
			
		||||
    void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
 | 
			
		||||
    double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
 | 
			
		||||
    void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Parent object owning this ModelVolume.
 | 
			
		||||
    ModelObject*            object;
 | 
			
		||||
| 
						 | 
				
			
			@ -300,12 +341,16 @@ public:
 | 
			
		|||
    friend class ModelObject;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    Geometry::Transformation m_transformation;
 | 
			
		||||
#else
 | 
			
		||||
    Vec3d m_offset;              // in unscaled coordinates
 | 
			
		||||
    Vec3d m_rotation;            // Rotation around the three axes, in radians around mesh center point
 | 
			
		||||
    Vec3d m_scaling_factor;      // Scaling factors along the three axes
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    Vec3d m_mirror;              // Mirroring along the three axes
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
 | 
			
		||||
| 
						 | 
				
			
			@ -313,6 +358,36 @@ public:
 | 
			
		|||
 | 
			
		||||
    ModelObject* get_object() const { return this->object; }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    const Geometry::Transformation& get_transformation() const { return m_transformation; }
 | 
			
		||||
    void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; }
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_offset() const { return m_transformation.get_offset(); }
 | 
			
		||||
    double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); }
 | 
			
		||||
    void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); }
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_rotation() const { return m_transformation.get_rotation(); }
 | 
			
		||||
    double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
 | 
			
		||||
    void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
 | 
			
		||||
 | 
			
		||||
    Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
 | 
			
		||||
    double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
 | 
			
		||||
    void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
 | 
			
		||||
    double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
 | 
			
		||||
    void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#else
 | 
			
		||||
    const Vec3d& get_offset() const { return m_offset; }
 | 
			
		||||
    double get_offset(Axis axis) const { return m_offset(axis); }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -343,6 +418,7 @@ public:
 | 
			
		|||
    void set_mirror(const Vec3d& mirror);
 | 
			
		||||
    void set_mirror(Axis axis, double mirror);
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    // To be called on an external mesh
 | 
			
		||||
    void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -355,11 +431,19 @@ public:
 | 
			
		|||
    // To be called on an external polygon. It does not translate the polygon, only rotates and scales.
 | 
			
		||||
    void transform_polygon(Polygon* polygon) const;
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    const Transform3d& world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.world_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
 | 
			
		||||
#else
 | 
			
		||||
    const Transform3d& world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const { return m_transformation.world_matrix(dont_translate, dont_rotate, dont_scale); }
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#else
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;
 | 
			
		||||
#else
 | 
			
		||||
    Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const;
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    bool is_printable() const { return print_volume_state == PVS_Inside; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -367,6 +451,11 @@ private:
 | 
			
		|||
    // Parent object, owning this instance.
 | 
			
		||||
    ModelObject* object;
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
 | 
			
		||||
    ModelInstance(ModelObject *object, const ModelInstance &other) :
 | 
			
		||||
        m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {}
 | 
			
		||||
#else
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {}
 | 
			
		||||
    ModelInstance(ModelObject *object, const ModelInstance &other) :
 | 
			
		||||
| 
						 | 
				
			
			@ -376,6 +465,7 @@ private:
 | 
			
		|||
    ModelInstance(ModelObject *object, const ModelInstance &other) :
 | 
			
		||||
        m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    explicit ModelInstance(ModelInstance &rhs) = delete;
 | 
			
		||||
    ModelInstance& operator=(ModelInstance &rhs) = delete;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,10 +19,13 @@
 | 
			
		|||
#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0)
 | 
			
		||||
// New selections
 | 
			
		||||
#define ENABLE_EXTENDED_SELECTION (1 && ENABLE_1_42_0)
 | 
			
		||||
#define DISABLE_INSTANCES_SYNCH (1 && ENABLE_EXTENDED_SELECTION)
 | 
			
		||||
// Add mirror components along the three axes in ModelInstance and GLVolume
 | 
			
		||||
#define ENABLE_MIRROR (1 && ENABLE_1_42_0)
 | 
			
		||||
// Modified camera target behavior
 | 
			
		||||
#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0)
 | 
			
		||||
// Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume
 | 
			
		||||
#define ENABLE_MODELVOLUME_TRANSFORM (1 && ENABLE_1_42_0)
 | 
			
		||||
 | 
			
		||||
#endif // _technologies_h_
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ void Bed_2D::repaint()
 | 
			
		|||
	dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxSOLID));
 | 
			
		||||
	for (auto pl : polylines)
 | 
			
		||||
	{
 | 
			
		||||
		for (size_t i = 0; i < pl.points.size()-1; i++){
 | 
			
		||||
		for (size_t i = 0; i < pl.points.size()-1; i++) {
 | 
			
		||||
			Point pt1 = to_pixels(unscale(pl.points[i]));
 | 
			
		||||
			Point pt2 = to_pixels(unscale(pl.points[i+1]));
 | 
			
		||||
			dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +110,7 @@ void Bed_2D::repaint()
 | 
			
		|||
	dc.SetPen(wxPen(wxColour(255, 0, 0), 2, wxSOLID));  // red
 | 
			
		||||
	auto x_end = Vec2d(origin_px(0) + axes_len, origin_px(1));
 | 
			
		||||
	dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(x_end(0), x_end(1)));
 | 
			
		||||
	for (auto angle : { -arrow_angle, arrow_angle }){
 | 
			
		||||
	for (auto angle : { -arrow_angle, arrow_angle }) {
 | 
			
		||||
		auto end = Eigen::Translation2d(x_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- x_end) * Eigen::Vector2d(x_end(0) - arrow_len, x_end(1));
 | 
			
		||||
		dc.DrawLine(wxPoint(x_end(0), x_end(1)), wxPoint(end(0), end(1)));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -151,12 +151,14 @@ void Bed_2D::repaint()
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// convert G - code coordinates into pixels
 | 
			
		||||
Point Bed_2D::to_pixels(Vec2d point){
 | 
			
		||||
Point Bed_2D::to_pixels(Vec2d point)
 | 
			
		||||
{
 | 
			
		||||
	auto p = point * m_scale_factor + m_shift;
 | 
			
		||||
	return Point(p(0), GetSize().GetHeight() - p(1)); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bed_2D::mouse_event(wxMouseEvent event){
 | 
			
		||||
void Bed_2D::mouse_event(wxMouseEvent event)
 | 
			
		||||
{
 | 
			
		||||
	if (!m_interactive) return;
 | 
			
		||||
	if (!m_painted) return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -170,11 +172,13 @@ void Bed_2D::mouse_event(wxMouseEvent event){
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// convert pixels into G - code coordinates
 | 
			
		||||
Vec2d Bed_2D::to_units(Point point){
 | 
			
		||||
Vec2d Bed_2D::to_units(Point point)
 | 
			
		||||
{
 | 
			
		||||
	return (Vec2d(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bed_2D::set_pos(Vec2d pos){
 | 
			
		||||
void Bed_2D::set_pos(Vec2d pos)
 | 
			
		||||
{
 | 
			
		||||
	m_pos = pos;
 | 
			
		||||
	Refresh();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,12 +34,12 @@ public:
 | 
			
		|||
#endif /*__APPLE__*/
 | 
			
		||||
		Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); }));
 | 
			
		||||
//		EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background};
 | 
			
		||||
//		Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent  event){/*mouse_event()*/; }));
 | 
			
		||||
		Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent  event){ mouse_event(event); }));
 | 
			
		||||
		Bind(wxEVT_MOTION, ([this](wxMouseEvent  event){ mouse_event(event); }));
 | 
			
		||||
//		Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent  event) {/*mouse_event()*/; }));
 | 
			
		||||
		Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent  event) { mouse_event(event); }));
 | 
			
		||||
		Bind(wxEVT_MOTION, ([this](wxMouseEvent  event) { mouse_event(event); }));
 | 
			
		||||
		Bind(wxEVT_SIZE, ([this](wxSizeEvent e) { Refresh(); }));
 | 
			
		||||
	}
 | 
			
		||||
	~Bed_2D(){}
 | 
			
		||||
	~Bed_2D() {}
 | 
			
		||||
 | 
			
		||||
	std::vector<Vec2d>		m_bed_shape;
 | 
			
		||||
		
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -195,6 +195,9 @@ const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
 | 
			
		|||
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
 | 
			
		||||
 | 
			
		||||
GLVolume::GLVolume(float r, float g, float b, float a)
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    : m_transformed_bounding_box_dirty(true)
 | 
			
		||||
#else
 | 
			
		||||
    : m_offset(Vec3d::Zero())
 | 
			
		||||
    , m_rotation(Vec3d::Zero())
 | 
			
		||||
    , m_scaling_factor(Vec3d::Ones())
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +207,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
 | 
			
		|||
    , m_world_matrix(Transform3f::Identity())
 | 
			
		||||
    , m_world_matrix_dirty(true)
 | 
			
		||||
    , m_transformed_bounding_box_dirty(true)
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    , m_transformed_convex_hull_bounding_box_dirty(true)
 | 
			
		||||
    , m_convex_hull(nullptr)
 | 
			
		||||
    , composite_id(-1)
 | 
			
		||||
| 
						 | 
				
			
			@ -260,6 +264,7 @@ void GLVolume::set_render_color()
 | 
			
		|||
        set_render_color(color, 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
const Vec3d& GLVolume::get_rotation() const
 | 
			
		||||
{
 | 
			
		||||
    return m_rotation;
 | 
			
		||||
| 
						 | 
				
			
			@ -360,6 +365,7 @@ void GLVolume::set_mirror(Axis axis, double mirror)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
void GLVolume::set_convex_hull(const TriangleMesh& convex_hull)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -386,6 +392,7 @@ void GLVolume::set_drag_group_id(const std::string& drag_by)
 | 
			
		|||
}
 | 
			
		||||
#endif // !ENABLE_EXTENDED_SELECTION
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
const Transform3f& GLVolume::world_matrix() const
 | 
			
		||||
{
 | 
			
		||||
    if (m_world_matrix_dirty)
 | 
			
		||||
| 
						 | 
				
			
			@ -399,12 +406,17 @@ const Transform3f& GLVolume::world_matrix() const
 | 
			
		|||
    }
 | 
			
		||||
    return m_world_matrix;
 | 
			
		||||
}
 | 
			
		||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
const BoundingBoxf3& GLVolume::transformed_bounding_box() const
 | 
			
		||||
{
 | 
			
		||||
    if (m_transformed_bounding_box_dirty)
 | 
			
		||||
    {
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        m_transformed_bounding_box = bounding_box.transformed(world_matrix());
 | 
			
		||||
#else
 | 
			
		||||
        m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast<double>());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        m_transformed_bounding_box_dirty = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -415,10 +427,17 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
 | 
			
		|||
{
 | 
			
		||||
    if (m_transformed_convex_hull_bounding_box_dirty)
 | 
			
		||||
    {
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0))
 | 
			
		||||
            m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix());
 | 
			
		||||
        else
 | 
			
		||||
            m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix());
 | 
			
		||||
#else
 | 
			
		||||
        if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0))
 | 
			
		||||
            m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast<double>());
 | 
			
		||||
        else
 | 
			
		||||
            m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast<double>());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
        m_transformed_convex_hull_bounding_box_dirty = false;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -469,7 +488,11 @@ void GLVolume::render() const
 | 
			
		|||
    ::glCullFace(GL_BACK);
 | 
			
		||||
    ::glPushMatrix();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    ::glMultMatrixd(world_matrix().data());
 | 
			
		||||
#else
 | 
			
		||||
    ::glMultMatrixf(world_matrix().data());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    if (this->indexed_vertex_array.indexed())
 | 
			
		||||
        this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
 | 
			
		||||
    else
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +530,11 @@ void GLVolume::render_using_layer_height() const
 | 
			
		|||
        glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
 | 
			
		||||
 | 
			
		||||
    if (world_matrix_id >= 0)
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data());
 | 
			
		||||
#else
 | 
			
		||||
        ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    GLsizei w = (GLsizei)layer_height_texture_width();
 | 
			
		||||
    GLsizei h = (GLsizei)layer_height_texture_height();
 | 
			
		||||
| 
						 | 
				
			
			@ -567,7 +594,11 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
 | 
			
		|||
            ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
        if (worldmatrix_id != -1)
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
            ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data());
 | 
			
		||||
#else
 | 
			
		||||
            ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
        render();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -586,7 +617,11 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
 | 
			
		|||
        ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
    if (worldmatrix_id != -1)
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data());
 | 
			
		||||
#else
 | 
			
		||||
        ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
 | 
			
		||||
    ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
 | 
			
		||||
| 
						 | 
				
			
			@ -594,7 +629,11 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
 | 
			
		|||
 | 
			
		||||
    ::glPushMatrix();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    ::glMultMatrixd(world_matrix().data());
 | 
			
		||||
#else
 | 
			
		||||
    ::glMultMatrixf(world_matrix().data());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    if (n_triangles > 0)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -638,7 +677,11 @@ void GLVolume::render_legacy() const
 | 
			
		|||
 | 
			
		||||
    ::glPushMatrix();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    ::glMultMatrixd(world_matrix().data());
 | 
			
		||||
#else
 | 
			
		||||
    ::glMultMatrixf(world_matrix().data());
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    if (n_triangles > 0)
 | 
			
		||||
        ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first);
 | 
			
		||||
| 
						 | 
				
			
			@ -767,12 +810,16 @@ std::vector<int> GLVolumeCollection::load_object(
 | 
			
		|||
            }
 | 
			
		||||
            v.is_modifier = ! model_volume->is_model_part();
 | 
			
		||||
            v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
            v.set_transformation(instance->get_transformation());
 | 
			
		||||
#else
 | 
			
		||||
            v.set_offset(instance->get_offset());
 | 
			
		||||
            v.set_rotation(instance->get_rotation());
 | 
			
		||||
            v.set_scaling_factor(instance->get_scaling_factor());
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
            v.set_mirror(instance->get_mirror());
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -254,6 +254,9 @@ public:
 | 
			
		|||
    GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    Geometry::Transformation m_transformation;
 | 
			
		||||
#else
 | 
			
		||||
    // Offset of the volume to be rendered.
 | 
			
		||||
    Vec3d                 m_offset;
 | 
			
		||||
    // Rotation around three axes of the volume to be rendered.
 | 
			
		||||
| 
						 | 
				
			
			@ -268,6 +271,7 @@ private:
 | 
			
		|||
    mutable Transform3f   m_world_matrix;
 | 
			
		||||
    // Whether or not is needed to recalculate the world matrix.
 | 
			
		||||
    mutable bool          m_world_matrix_dirty;
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    // Bounding box of this volume, in unscaled coordinates.
 | 
			
		||||
    mutable BoundingBoxf3 m_transformed_bounding_box;
 | 
			
		||||
    // Whether or not is needed to recalculate the transformed bounding box.
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +284,6 @@ private:
 | 
			
		|||
    mutable bool          m_transformed_convex_hull_bounding_box_dirty;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    // Bounding box of this volume, in unscaled coordinates.
 | 
			
		||||
    BoundingBoxf3       bounding_box;
 | 
			
		||||
    // Color of the triangles / quads held by this volume.
 | 
			
		||||
| 
						 | 
				
			
			@ -333,6 +336,36 @@ public:
 | 
			
		|||
    // Sets render color in dependence of current state
 | 
			
		||||
    void set_render_color();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    const Geometry::Transformation& get_transformation() const { return m_transformation; }
 | 
			
		||||
    void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; set_bounding_boxes_as_dirty(); }
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_offset() const { return m_transformation.get_offset(); }
 | 
			
		||||
    double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
    void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
 | 
			
		||||
    const Vec3d& get_rotation() const { return m_transformation.get_rotation(); }
 | 
			
		||||
    double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
    void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
 | 
			
		||||
    Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
 | 
			
		||||
    double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
    void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MIRROR
 | 
			
		||||
    const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
 | 
			
		||||
    double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
 | 
			
		||||
 | 
			
		||||
    void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
    void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); }
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
#else
 | 
			
		||||
    const Vec3d& get_rotation() const;
 | 
			
		||||
    void set_rotation(const Vec3d& rotation);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -350,6 +383,7 @@ public:
 | 
			
		|||
 | 
			
		||||
    const Vec3d& get_offset() const;
 | 
			
		||||
    void set_offset(const Vec3d& offset);
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
    void set_convex_hull(const TriangleMesh& convex_hull);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -362,7 +396,11 @@ public:
 | 
			
		|||
    int                 volume_idx() const { return (this->composite_id / 1000) % 1000; }
 | 
			
		||||
    int                 instance_idx() const { return this->composite_id % 1000; }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    const Transform3d&   world_matrix() const { return m_transformation.world_matrix(); }
 | 
			
		||||
#else
 | 
			
		||||
    const Transform3f&   world_matrix() const;
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    const BoundingBoxf3& transformed_bounding_box() const;
 | 
			
		||||
    const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -412,6 +450,10 @@ public:
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void reset_layer_height_texture_data() { layer_height_texture_data.reset(); }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if ENABLE_EXTENDED_SELECTION
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
 | 
			
		|||
	main_sizer->SetSizeHints(this);
 | 
			
		||||
 | 
			
		||||
	// needed to actually free memory
 | 
			
		||||
	this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e){
 | 
			
		||||
	this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e) {
 | 
			
		||||
		EndModal(wxID_OK);
 | 
			
		||||
		Destroy();
 | 
			
		||||
	}));
 | 
			
		||||
| 
						 | 
				
			
			@ -115,14 +115,15 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
 | 
			
		|||
 | 
			
		||||
// Called from the constructor.
 | 
			
		||||
// Create a panel for a rectangular / circular / custom bed shape.
 | 
			
		||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title){
 | 
			
		||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	auto panel = new wxPanel(m_shape_options_book);
 | 
			
		||||
	ConfigOptionsGroupShp optgroup;
 | 
			
		||||
	optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
 | 
			
		||||
 | 
			
		||||
	optgroup->label_width = 100;
 | 
			
		||||
	optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
	optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
		update_shape();
 | 
			
		||||
	};
 | 
			
		||||
		
 | 
			
		||||
| 
						 | 
				
			
			@ -234,14 +235,15 @@ void BedShapePanel::update_shape()
 | 
			
		|||
		Vec2d rect_origin(Vec2d::Zero());
 | 
			
		||||
		try{
 | 
			
		||||
			rect_size = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); }
 | 
			
		||||
		catch (const std::exception & /* e */){
 | 
			
		||||
		catch (const std::exception & /* e */) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		try{
 | 
			
		||||
		try {
 | 
			
		||||
			rect_origin = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin"));
 | 
			
		||||
		}
 | 
			
		||||
		catch (const std::exception & /* e */){
 | 
			
		||||
			return;}
 | 
			
		||||
		catch (const std::exception & /* e */) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		auto x = rect_size(0);
 | 
			
		||||
		auto y = rect_size(1);
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +271,7 @@ void BedShapePanel::update_shape()
 | 
			
		|||
		try{
 | 
			
		||||
			diameter = boost::any_cast<double>(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter"));
 | 
			
		||||
		}
 | 
			
		||||
		catch (const std::exception & /* e */){
 | 
			
		||||
		catch (const std::exception & /* e */) {
 | 
			
		||||
			return;
 | 
			
		||||
		} 
 | 
			
		||||
 		if (diameter == 0.0) return ;
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +279,7 @@ void BedShapePanel::update_shape()
 | 
			
		|||
		auto twopi = 2 * PI;
 | 
			
		||||
		auto edges = 60;
 | 
			
		||||
		std::vector<Vec2d> points;
 | 
			
		||||
		for (size_t i = 1; i <= 60; ++i){
 | 
			
		||||
		for (size_t i = 1; i <= 60; ++i) {
 | 
			
		||||
			auto angle = i * twopi / edges;
 | 
			
		||||
			points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,8 +22,8 @@ class BedShapePanel : public wxPanel
 | 
			
		|||
	std::vector <ConfigOptionsGroupShp>	m_optgroups;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY){}
 | 
			
		||||
	~BedShapePanel(){}
 | 
			
		||||
	BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
 | 
			
		||||
	~BedShapePanel() {}
 | 
			
		||||
 | 
			
		||||
	void		build_panel(ConfigOptionPoints* default_pt);
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -42,8 +42,8 @@ class BedShapeDialog : public wxDialog
 | 
			
		|||
	BedShapePanel*	m_panel;
 | 
			
		||||
public:
 | 
			
		||||
	BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _(L("Bed Shape")),
 | 
			
		||||
		wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER){}
 | 
			
		||||
	~BedShapeDialog(){  }
 | 
			
		||||
		wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
 | 
			
		||||
	~BedShapeDialog() {}
 | 
			
		||||
 | 
			
		||||
	void		build_dialog(ConfigOptionPoints* default_pt);
 | 
			
		||||
	std::vector<Vec2d>	GetValue() { return m_panel->GetValue(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* ic
 | 
			
		|||
		sys_label->Refresh();
 | 
			
		||||
	}));
 | 
			
		||||
	size_t t= 0;
 | 
			
		||||
	while (t < 3){
 | 
			
		||||
	while (t < 3) {
 | 
			
		||||
		grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
 | 
			
		||||
		++t;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ class ButtonsDescription : public wxDialog
 | 
			
		|||
	t_icon_descriptions* m_icon_descriptions;
 | 
			
		||||
public:
 | 
			
		||||
	ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions);
 | 
			
		||||
	~ButtonsDescription(){}
 | 
			
		||||
	~ButtonsDescription() {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,270 +11,275 @@
 | 
			
		|||
 | 
			
		||||
namespace Slic3r { namespace GUI {
 | 
			
		||||
 | 
			
		||||
	wxString double_to_string(double const value)
 | 
			
		||||
	{
 | 
			
		||||
		if (value - int(value) == 0)
 | 
			
		||||
			return wxString::Format(_T("%i"), int(value));
 | 
			
		||||
		else {
 | 
			
		||||
			int precision = 4;
 | 
			
		||||
			for (size_t p = 1; p < 4; p++)
 | 
			
		||||
			{
 | 
			
		||||
				double cur_val = pow(10, p)*value;
 | 
			
		||||
				if (cur_val - int(cur_val) == 0) {
 | 
			
		||||
					precision = p;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Field::PostInitialize(){
 | 
			
		||||
		auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 | 
			
		||||
		m_Undo_btn			= new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
 | 
			
		||||
		m_Undo_to_sys_btn	= new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
 | 
			
		||||
		if (wxMSW) {
 | 
			
		||||
			m_Undo_btn->SetBackgroundColour(color);
 | 
			
		||||
			m_Undo_to_sys_btn->SetBackgroundColour(color);
 | 
			
		||||
		}
 | 
			
		||||
		m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); }));
 | 
			
		||||
		m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); }));
 | 
			
		||||
 | 
			
		||||
		//set default bitmap
 | 
			
		||||
		wxBitmap bmp;
 | 
			
		||||
		bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
		set_undo_bitmap(&bmp);
 | 
			
		||||
		set_undo_to_sys_bitmap(&bmp);
 | 
			
		||||
 | 
			
		||||
		switch (m_opt.type)
 | 
			
		||||
wxString double_to_string(double const value)
 | 
			
		||||
{
 | 
			
		||||
	if (value - int(value) == 0)
 | 
			
		||||
		return wxString::Format(_T("%i"), int(value));
 | 
			
		||||
	else {
 | 
			
		||||
		int precision = 4;
 | 
			
		||||
		for (size_t p = 1; p < 4; p++)
 | 
			
		||||
		{
 | 
			
		||||
		case coPercents:
 | 
			
		||||
		case coFloats:
 | 
			
		||||
		case coStrings:	
 | 
			
		||||
		case coBools:		
 | 
			
		||||
		case coInts: {
 | 
			
		||||
			auto tag_pos = m_opt_id.find("#");
 | 
			
		||||
			if (tag_pos != std::string::npos)
 | 
			
		||||
				m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size()));
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		BUILD();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Field::on_kill_focus(wxEvent& event) {
 | 
			
		||||
        // Without this, there will be nasty focus bugs on Windows.
 | 
			
		||||
        // Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all 
 | 
			
		||||
        // non-command events to allow the default handling to take place."
 | 
			
		||||
		event.Skip();
 | 
			
		||||
		// call the registered function if it is available
 | 
			
		||||
        if (m_on_kill_focus!=nullptr) 
 | 
			
		||||
            m_on_kill_focus();
 | 
			
		||||
    }
 | 
			
		||||
    void Field::on_change_field()
 | 
			
		||||
	{
 | 
			
		||||
//        std::cerr << "calling Field::_on_change \n";
 | 
			
		||||
        if (m_on_change != nullptr  && !m_disable_change_event)
 | 
			
		||||
            m_on_change(m_opt_id, get_value());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	void Field::on_back_to_initial_value(){
 | 
			
		||||
		if (m_back_to_initial_value != nullptr && m_is_modified_value)
 | 
			
		||||
			m_back_to_initial_value(m_opt_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Field::on_back_to_sys_value(){
 | 
			
		||||
		if (m_back_to_sys_value != nullptr && m_is_nonsys_value)
 | 
			
		||||
			m_back_to_sys_value(m_opt_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wxString Field::get_tooltip_text(const wxString& default_string)
 | 
			
		||||
	{
 | 
			
		||||
		wxString tooltip_text("");
 | 
			
		||||
		wxString tooltip = _(m_opt.tooltip);
 | 
			
		||||
		if (tooltip.length() > 0)
 | 
			
		||||
            tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " +
 | 
			
		||||
            (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string +
 | 
			
		||||
            (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + 
 | 
			
		||||
            _(L("parameter name")) + "\t: " + m_opt_id;
 | 
			
		||||
 | 
			
		||||
		return tooltip_text;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Field::is_matched(const std::string& string, const std::string& pattern)
 | 
			
		||||
	{
 | 
			
		||||
		std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl
 | 
			
		||||
		return std::regex_match(string, regex_pattern);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Field::get_value_by_opt_type(wxString& str)
 | 
			
		||||
	{
 | 
			
		||||
		switch (m_opt.type){
 | 
			
		||||
		case coInt:
 | 
			
		||||
			m_value = wxAtoi(str);
 | 
			
		||||
			break;
 | 
			
		||||
		case coPercent:
 | 
			
		||||
		case coPercents:
 | 
			
		||||
		case coFloats:
 | 
			
		||||
		case coFloat:{
 | 
			
		||||
			if (m_opt.type == coPercent && str.Last() == '%') 
 | 
			
		||||
				str.RemoveLast();
 | 
			
		||||
			else if (str.Last() == '%')	{
 | 
			
		||||
				wxString label = m_Label->GetLabel();
 | 
			
		||||
				if		(label.Last() == '\n')	label.RemoveLast();
 | 
			
		||||
				while	(label.Last() == ' ')	label.RemoveLast();
 | 
			
		||||
				if		(label.Last() == ':')	label.RemoveLast();
 | 
			
		||||
				show_error(m_parent, wxString::Format(_(L("%s doesn't support percentage")), label));
 | 
			
		||||
				set_value(double_to_string(m_opt.min), true);
 | 
			
		||||
				m_value = double(m_opt.min);
 | 
			
		||||
			double cur_val = pow(10, p)*value;
 | 
			
		||||
			if (cur_val - int(cur_val) == 0) {
 | 
			
		||||
				precision = p;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			double val;
 | 
			
		||||
			if(!str.ToCDouble(&val))
 | 
			
		||||
			{
 | 
			
		||||
				show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
 | 
			
		||||
				set_value(double_to_string(val), true);
 | 
			
		||||
			}
 | 
			
		||||
			if (m_opt.min > val || val > m_opt.max)
 | 
			
		||||
			{
 | 
			
		||||
				show_error(m_parent, _(L("Input value is out of range")));
 | 
			
		||||
				if (m_opt.min > val) val = m_opt.min;
 | 
			
		||||
				if (val > m_opt.max) val = m_opt.max;
 | 
			
		||||
				set_value(double_to_string(val), true);
 | 
			
		||||
			}
 | 
			
		||||
			m_value = val;
 | 
			
		||||
			break; }
 | 
			
		||||
		case coString:
 | 
			
		||||
		case coStrings:
 | 
			
		||||
		case coFloatOrPercent:
 | 
			
		||||
			m_value = str.ToStdString();
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Field::PostInitialize()
 | 
			
		||||
{
 | 
			
		||||
	auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 | 
			
		||||
	m_Undo_btn			= new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
 | 
			
		||||
	m_Undo_to_sys_btn	= new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
 | 
			
		||||
	if (wxMSW) {
 | 
			
		||||
		m_Undo_btn->SetBackgroundColour(color);
 | 
			
		||||
		m_Undo_to_sys_btn->SetBackgroundColour(color);
 | 
			
		||||
	}
 | 
			
		||||
	m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); }));
 | 
			
		||||
	m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); }));
 | 
			
		||||
 | 
			
		||||
	//set default bitmap
 | 
			
		||||
	wxBitmap bmp;
 | 
			
		||||
	bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
	set_undo_bitmap(&bmp);
 | 
			
		||||
	set_undo_to_sys_bitmap(&bmp);
 | 
			
		||||
 | 
			
		||||
	switch (m_opt.type)
 | 
			
		||||
	{
 | 
			
		||||
	case coPercents:
 | 
			
		||||
	case coFloats:
 | 
			
		||||
	case coStrings:	
 | 
			
		||||
	case coBools:		
 | 
			
		||||
	case coInts: {
 | 
			
		||||
		auto tag_pos = m_opt_id.find("#");
 | 
			
		||||
		if (tag_pos != std::string::npos)
 | 
			
		||||
			m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size()));
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void TextCtrl::BUILD() {
 | 
			
		||||
        auto size = wxSize(wxDefaultSize);
 | 
			
		||||
        if (m_opt.height >= 0) size.SetHeight(m_opt.height);
 | 
			
		||||
        if (m_opt.width >= 0) size.SetWidth(m_opt.width);
 | 
			
		||||
	BUILD();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		wxString text_value = wxString(""); 
 | 
			
		||||
void Field::on_kill_focus(wxEvent& event)
 | 
			
		||||
{
 | 
			
		||||
    // Without this, there will be nasty focus bugs on Windows.
 | 
			
		||||
    // Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all 
 | 
			
		||||
    // non-command events to allow the default handling to take place."
 | 
			
		||||
	event.Skip();
 | 
			
		||||
	// call the registered function if it is available
 | 
			
		||||
    if (m_on_kill_focus!=nullptr) 
 | 
			
		||||
        m_on_kill_focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		switch (m_opt.type) {
 | 
			
		||||
		case coFloatOrPercent:
 | 
			
		||||
		{
 | 
			
		||||
			text_value = double_to_string(m_opt.default_value->getFloat());
 | 
			
		||||
 			if (static_cast<const ConfigOptionFloatOrPercent*>(m_opt.default_value)->percent)
 | 
			
		||||
 				text_value += "%";
 | 
			
		||||
void Field::on_change_field()
 | 
			
		||||
{
 | 
			
		||||
      std::cerr << "calling Field::_on_change \n";
 | 
			
		||||
    if (m_on_change != nullptr  && !m_disable_change_event)
 | 
			
		||||
        m_on_change(m_opt_id, get_value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Field::on_back_to_initial_value()
 | 
			
		||||
{
 | 
			
		||||
	if (m_back_to_initial_value != nullptr && m_is_modified_value)
 | 
			
		||||
		m_back_to_initial_value(m_opt_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Field::on_back_to_sys_value()
 | 
			
		||||
{
 | 
			
		||||
	if (m_back_to_sys_value != nullptr && m_is_nonsys_value)
 | 
			
		||||
		m_back_to_sys_value(m_opt_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxString Field::get_tooltip_text(const wxString& default_string)
 | 
			
		||||
{
 | 
			
		||||
	wxString tooltip_text("");
 | 
			
		||||
	wxString tooltip = _(m_opt.tooltip);
 | 
			
		||||
	if (tooltip.length() > 0)
 | 
			
		||||
        tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " +
 | 
			
		||||
        (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string +
 | 
			
		||||
        (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + 
 | 
			
		||||
        _(L("parameter name")) + "\t: " + m_opt_id;
 | 
			
		||||
 | 
			
		||||
	return tooltip_text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Field::is_matched(const std::string& string, const std::string& pattern)
 | 
			
		||||
{
 | 
			
		||||
	std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl
 | 
			
		||||
	return std::regex_match(string, regex_pattern);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Field::get_value_by_opt_type(wxString& str)
 | 
			
		||||
{
 | 
			
		||||
	switch (m_opt.type) {
 | 
			
		||||
	case coInt:
 | 
			
		||||
		m_value = wxAtoi(str);
 | 
			
		||||
		break;
 | 
			
		||||
	case coPercent:
 | 
			
		||||
	case coPercents:
 | 
			
		||||
	case coFloats:
 | 
			
		||||
	case coFloat:{
 | 
			
		||||
		if (m_opt.type == coPercent && str.Last() == '%') 
 | 
			
		||||
			str.RemoveLast();
 | 
			
		||||
		else if (str.Last() == '%')	{
 | 
			
		||||
			wxString label = m_Label->GetLabel();
 | 
			
		||||
			if		(label.Last() == '\n')	label.RemoveLast();
 | 
			
		||||
			while	(label.Last() == ' ')	label.RemoveLast();
 | 
			
		||||
			if		(label.Last() == ':')	label.RemoveLast();
 | 
			
		||||
			show_error(m_parent, wxString::Format(_(L("%s doesn't support percentage")), label));
 | 
			
		||||
			set_value(double_to_string(m_opt.min), true);
 | 
			
		||||
			m_value = double(m_opt.min);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case coPercent:
 | 
			
		||||
		double val;
 | 
			
		||||
		if(!str.ToCDouble(&val))
 | 
			
		||||
		{
 | 
			
		||||
			text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat()));
 | 
			
		||||
			show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
 | 
			
		||||
			set_value(double_to_string(val), true);
 | 
			
		||||
		}
 | 
			
		||||
		if (m_opt.min > val || val > m_opt.max)
 | 
			
		||||
		{
 | 
			
		||||
			show_error(m_parent, _(L("Input value is out of range")));
 | 
			
		||||
			if (m_opt.min > val) val = m_opt.min;
 | 
			
		||||
			if (val > m_opt.max) val = m_opt.max;
 | 
			
		||||
			set_value(double_to_string(val), true);
 | 
			
		||||
		}
 | 
			
		||||
		m_value = val;
 | 
			
		||||
		break; }
 | 
			
		||||
	case coString:
 | 
			
		||||
	case coStrings:
 | 
			
		||||
	case coFloatOrPercent:
 | 
			
		||||
		m_value = str.ToStdString();
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TextCtrl::BUILD() {
 | 
			
		||||
    auto size = wxSize(wxDefaultSize);
 | 
			
		||||
    if (m_opt.height >= 0) size.SetHeight(m_opt.height);
 | 
			
		||||
    if (m_opt.width >= 0) size.SetWidth(m_opt.width);
 | 
			
		||||
 | 
			
		||||
	wxString text_value = wxString(""); 
 | 
			
		||||
 | 
			
		||||
	switch (m_opt.type) {
 | 
			
		||||
	case coFloatOrPercent:
 | 
			
		||||
	{
 | 
			
		||||
		text_value = double_to_string(m_opt.default_value->getFloat());
 | 
			
		||||
		if (static_cast<const ConfigOptionFloatOrPercent*>(m_opt.default_value)->percent)
 | 
			
		||||
			text_value += "%";
 | 
			
		||||
			break;
 | 
			
		||||
		}	
 | 
			
		||||
		case coPercents:
 | 
			
		||||
		case coFloats:
 | 
			
		||||
		case coFloat:
 | 
			
		||||
		{
 | 
			
		||||
			double val = m_opt.type == coFloats ?
 | 
			
		||||
				static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(m_opt_idx) :
 | 
			
		||||
				m_opt.type == coFloat ? 
 | 
			
		||||
					m_opt.default_value->getFloat() :
 | 
			
		||||
					static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(m_opt_idx);
 | 
			
		||||
			text_value = double_to_string(val);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case coString:			
 | 
			
		||||
			text_value = static_cast<const ConfigOptionString*>(m_opt.default_value)->value;
 | 
			
		||||
			break;
 | 
			
		||||
		case coStrings:
 | 
			
		||||
		{
 | 
			
		||||
			const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value);
 | 
			
		||||
			if (vec == nullptr || vec->empty()) break; //for the case of empty default value
 | 
			
		||||
			text_value = vec->get_at(m_opt_idx);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		default:
 | 
			
		||||
			break; 
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	case coPercent:
 | 
			
		||||
	{
 | 
			
		||||
		text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat()));
 | 
			
		||||
		text_value += "%";
 | 
			
		||||
		break;
 | 
			
		||||
	}	
 | 
			
		||||
	case coPercents:
 | 
			
		||||
	case coFloats:
 | 
			
		||||
	case coFloat:
 | 
			
		||||
	{
 | 
			
		||||
		double val = m_opt.type == coFloats ?
 | 
			
		||||
			static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(m_opt_idx) :
 | 
			
		||||
			m_opt.type == coFloat ? 
 | 
			
		||||
				m_opt.default_value->getFloat() :
 | 
			
		||||
				static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(m_opt_idx);
 | 
			
		||||
		text_value = double_to_string(val);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	case coString:			
 | 
			
		||||
		text_value = static_cast<const ConfigOptionString*>(m_opt.default_value)->value;
 | 
			
		||||
		break;
 | 
			
		||||
	case coStrings:
 | 
			
		||||
	{
 | 
			
		||||
		const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value);
 | 
			
		||||
		if (vec == nullptr || vec->empty()) break; //for the case of empty default value
 | 
			
		||||
		text_value = vec->get_at(m_opt_idx);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		break; 
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, (m_opt.multiline ? wxTE_MULTILINE : 0));
 | 
			
		||||
	auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, (m_opt.multiline ? wxTE_MULTILINE : 0));
 | 
			
		||||
 | 
			
		||||
		temp->SetToolTip(get_tooltip_text(text_value));
 | 
			
		||||
        
 | 
			
		||||
		temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event)
 | 
			
		||||
		{
 | 
			
		||||
			//! to allow the default handling
 | 
			
		||||
			event.Skip();
 | 
			
		||||
			//! eliminating the g-code pop up text description
 | 
			
		||||
			bool flag = false;
 | 
			
		||||
	temp->SetToolTip(get_tooltip_text(text_value));
 | 
			
		||||
    
 | 
			
		||||
	temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event)
 | 
			
		||||
	{
 | 
			
		||||
		//! to allow the default handling
 | 
			
		||||
		event.Skip();
 | 
			
		||||
		//! eliminating the g-code pop up text description
 | 
			
		||||
		bool flag = false;
 | 
			
		||||
#ifdef __WXGTK__
 | 
			
		||||
			// I have no idea why, but on GTK flag works in other way
 | 
			
		||||
			flag = true;
 | 
			
		||||
		// I have no idea why, but on GTK flag works in other way
 | 
			
		||||
		flag = true;
 | 
			
		||||
#endif // __WXGTK__
 | 
			
		||||
			temp->GetToolTip()->Enable(flag);
 | 
			
		||||
		}), temp->GetId());
 | 
			
		||||
		temp->GetToolTip()->Enable(flag);
 | 
			
		||||
	}), temp->GetId());
 | 
			
		||||
 | 
			
		||||
#if !defined(__WXGTK__)
 | 
			
		||||
		temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
 | 
			
		||||
		{
 | 
			
		||||
			e.Skip();//	on_kill_focus(e);
 | 
			
		||||
			temp->GetToolTip()->Enable(true);
 | 
			
		||||
		}), temp->GetId());
 | 
			
		||||
	temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
 | 
			
		||||
	{
 | 
			
		||||
		e.Skip();//	on_kill_focus(e);
 | 
			
		||||
		temp->GetToolTip()->Enable(true);
 | 
			
		||||
	}), temp->GetId());
 | 
			
		||||
#endif // __WXGTK__
 | 
			
		||||
 | 
			
		||||
		temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
 | 
			
		||||
		{
 | 
			
		||||
#ifdef __WXGTK__
 | 
			
		||||
			if (bChangedValueEvent)
 | 
			
		||||
#endif //__WXGTK__
 | 
			
		||||
			on_change_field();
 | 
			
		||||
		}), temp->GetId());
 | 
			
		||||
 | 
			
		||||
#ifdef __WXGTK__
 | 
			
		||||
        // to correct value updating on GTK we should:
 | 
			
		||||
        // call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT
 | 
			
		||||
        // and prevent value updating on wxEVT_KEY_DOWN
 | 
			
		||||
        temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
 | 
			
		||||
        temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
 | 
			
		||||
#endif //__WXGTK__
 | 
			
		||||
 | 
			
		||||
		// select all text using Ctrl+A
 | 
			
		||||
		temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event)
 | 
			
		||||
		{
 | 
			
		||||
			if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
 | 
			
		||||
				temp->SetSelection(-1, -1); //select all
 | 
			
		||||
			event.Skip();
 | 
			
		||||
		}));
 | 
			
		||||
 | 
			
		||||
        // recast as a wxWindow to fit the calling convention
 | 
			
		||||
        window = dynamic_cast<wxWindow*>(temp);
 | 
			
		||||
    }	
 | 
			
		||||
 | 
			
		||||
	boost::any& TextCtrl::get_value()
 | 
			
		||||
	temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
 | 
			
		||||
	{
 | 
			
		||||
		wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
 | 
			
		||||
		get_value_by_opt_type(ret_str);
 | 
			
		||||
 | 
			
		||||
		return m_value;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
 | 
			
		||||
    void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(false); }
 | 
			
		||||
#ifdef __WXGTK__
 | 
			
		||||
        if (bChangedValueEvent)
 | 
			
		||||
#endif //__WXGTK__
 | 
			
		||||
		on_change_field();
 | 
			
		||||
	}), temp->GetId());
 | 
			
		||||
 | 
			
		||||
#ifdef __WXGTK__
 | 
			
		||||
    void TextCtrl::change_field_value(wxEvent& event)
 | 
			
		||||
    {
 | 
			
		||||
    	if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP)
 | 
			
		||||
    		on_change_field();
 | 
			
		||||
        event.Skip();
 | 
			
		||||
    };
 | 
			
		||||
    // to correct value updating on GTK we should:
 | 
			
		||||
    // call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT
 | 
			
		||||
    // and prevent value updating on wxEVT_KEY_DOWN
 | 
			
		||||
    temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
 | 
			
		||||
    temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
 | 
			
		||||
#endif //__WXGTK__
 | 
			
		||||
 | 
			
		||||
	// select all text using Ctrl+A
 | 
			
		||||
	temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event)
 | 
			
		||||
	{
 | 
			
		||||
		if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
 | 
			
		||||
			temp->SetSelection(-1, -1); //select all
 | 
			
		||||
		event.Skip();
 | 
			
		||||
	}));
 | 
			
		||||
 | 
			
		||||
    // recast as a wxWindow to fit the calling convention
 | 
			
		||||
    window = dynamic_cast<wxWindow*>(temp);
 | 
			
		||||
}	
 | 
			
		||||
 | 
			
		||||
boost::any& TextCtrl::get_value()
 | 
			
		||||
{
 | 
			
		||||
	wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
 | 
			
		||||
	get_value_by_opt_type(ret_str);
 | 
			
		||||
 | 
			
		||||
	return m_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
 | 
			
		||||
void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(false); }
 | 
			
		||||
 | 
			
		||||
#ifdef __WXGTK__
 | 
			
		||||
void TextCtrl::change_field_value(wxEvent& event)
 | 
			
		||||
{
 | 
			
		||||
	if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP)
 | 
			
		||||
		on_change_field();
 | 
			
		||||
    event.Skip();
 | 
			
		||||
};
 | 
			
		||||
#endif //__WXGTK__
 | 
			
		||||
 | 
			
		||||
void CheckBox::BUILD() {
 | 
			
		||||
| 
						 | 
				
			
			@ -384,10 +389,10 @@ void Choice::BUILD() {
 | 
			
		|||
	// recast as a wxWindow to fit the calling convention
 | 
			
		||||
	window = dynamic_cast<wxWindow*>(temp);
 | 
			
		||||
 | 
			
		||||
	if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()){
 | 
			
		||||
	if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()) {
 | 
			
		||||
	}
 | 
			
		||||
	else{
 | 
			
		||||
		for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){
 | 
			
		||||
		for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels) {
 | 
			
		||||
			const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el;
 | 
			
		||||
			temp->Append(str);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +407,7 @@ void Choice::BUILD() {
 | 
			
		|||
void Choice::set_selection()
 | 
			
		||||
{
 | 
			
		||||
	wxString text_value = wxString("");
 | 
			
		||||
	switch (m_opt.type){
 | 
			
		||||
	switch (m_opt.type) {
 | 
			
		||||
	case coFloat:
 | 
			
		||||
	case coPercent:	{
 | 
			
		||||
		double val = m_opt.default_value->getFloat();
 | 
			
		||||
| 
						 | 
				
			
			@ -481,12 +486,12 @@ void Choice::set_value(const boost::any& value, bool change_event)
 | 
			
		|||
{
 | 
			
		||||
	m_disable_change_event = !change_event;
 | 
			
		||||
 | 
			
		||||
	switch (m_opt.type){
 | 
			
		||||
	switch (m_opt.type) {
 | 
			
		||||
	case coInt:
 | 
			
		||||
	case coFloat:
 | 
			
		||||
	case coPercent:
 | 
			
		||||
	case coString:
 | 
			
		||||
	case coStrings:{
 | 
			
		||||
	case coStrings: {
 | 
			
		||||
		wxString text_value;
 | 
			
		||||
		if (m_opt.type == coInt) 
 | 
			
		||||
			text_value = wxString::Format(_T("%i"), int(boost::any_cast<int>(value)));
 | 
			
		||||
| 
						 | 
				
			
			@ -504,11 +509,11 @@ void Choice::set_value(const boost::any& value, bool change_event)
 | 
			
		|||
			dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	case coEnum:{
 | 
			
		||||
	case coEnum: {
 | 
			
		||||
		int val = boost::any_cast<int>(value);
 | 
			
		||||
		if (m_opt_id.compare("external_fill_pattern") == 0)
 | 
			
		||||
		{
 | 
			
		||||
			if (!m_opt.enum_values.empty()){
 | 
			
		||||
			if (!m_opt.enum_values.empty()) {
 | 
			
		||||
				std::string key;
 | 
			
		||||
				t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();				
 | 
			
		||||
				for (auto it : map_names) {
 | 
			
		||||
| 
						 | 
				
			
			@ -579,7 +584,7 @@ boost::any& Choice::get_value()
 | 
			
		|||
		int ret_enum = static_cast<wxComboBox*>(window)->GetSelection(); 
 | 
			
		||||
		if (m_opt_id.compare("external_fill_pattern") == 0)
 | 
			
		||||
		{
 | 
			
		||||
			if (!m_opt.enum_values.empty()){
 | 
			
		||||
			if (!m_opt.enum_values.empty()) {
 | 
			
		||||
				std::string key = m_opt.enum_values[ret_enum];
 | 
			
		||||
				t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
 | 
			
		||||
				int value = map_names.at(key);
 | 
			
		||||
| 
						 | 
				
			
			@ -627,7 +632,8 @@ void ColourPicker::BUILD()
 | 
			
		|||
	temp->SetToolTip(get_tooltip_text(clr_str));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boost::any& ColourPicker::get_value(){
 | 
			
		||||
boost::any& ColourPicker::get_value()
 | 
			
		||||
{
 | 
			
		||||
// 	boost::any m_value;
 | 
			
		||||
 | 
			
		||||
	auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour();
 | 
			
		||||
| 
						 | 
				
			
			@ -746,7 +752,7 @@ void SliderCtrl::BUILD()
 | 
			
		|||
	temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);
 | 
			
		||||
 | 
			
		||||
	m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) {
 | 
			
		||||
		if (!m_disable_change_event){
 | 
			
		||||
		if (!m_disable_change_event) {
 | 
			
		||||
			int val = boost::any_cast<int>(get_value());
 | 
			
		||||
			m_textctrl->SetLabel(wxString::Format("%d", val));
 | 
			
		||||
			on_change_field();
 | 
			
		||||
| 
						 | 
				
			
			@ -755,7 +761,7 @@ void SliderCtrl::BUILD()
 | 
			
		|||
 | 
			
		||||
	m_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) {
 | 
			
		||||
		std::string value = e.GetString().utf8_str().data();
 | 
			
		||||
		if (is_matched(value, "^-?\\d+(\\.\\d*)?$")){
 | 
			
		||||
		if (is_matched(value, "^-?\\d+(\\.\\d*)?$")) {
 | 
			
		||||
			m_disable_change_event = true;
 | 
			
		||||
			m_slider->SetValue(stoi(value)*m_scale);
 | 
			
		||||
			m_disable_change_event = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1426,8 +1426,10 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation)
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if !DISABLE_INSTANCES_SYNCH
 | 
			
		||||
    if (m_mode == Instance)
 | 
			
		||||
        _synchronize_unselected_instances();
 | 
			
		||||
#endif // !DISABLE_INSTANCES_SYNCH
 | 
			
		||||
 | 
			
		||||
    m_bounding_box_dirty = true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1453,8 +1455,10 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale)
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if !DISABLE_INSTANCES_SYNCH
 | 
			
		||||
    if (m_mode == Instance)
 | 
			
		||||
        _synchronize_unselected_instances();
 | 
			
		||||
#endif // !DISABLE_INSTANCES_SYNCH
 | 
			
		||||
 | 
			
		||||
    m_bounding_box_dirty = true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1471,13 +1475,105 @@ void GLCanvas3D::Selection::mirror(Axis axis)
 | 
			
		|||
            (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if !DISABLE_INSTANCES_SYNCH
 | 
			
		||||
    if (m_mode == Instance)
 | 
			
		||||
        _synchronize_unselected_instances();
 | 
			
		||||
#endif // !DISABLE_INSTANCES_SYNCH
 | 
			
		||||
 | 
			
		||||
    m_bounding_box_dirty = true;
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& displacement)
 | 
			
		||||
{
 | 
			
		||||
    if (!m_valid)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i : m_list)
 | 
			
		||||
    {
 | 
			
		||||
        GLVolume* v = (*m_volumes)[i];
 | 
			
		||||
        if (v->object_idx() == object_idx)
 | 
			
		||||
            v->set_offset(v->get_offset() + displacement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::set<unsigned int> done;  // prevent processing volumes twice
 | 
			
		||||
    done.insert(m_list.begin(), m_list.end());
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i : m_list)
 | 
			
		||||
    {
 | 
			
		||||
        if (done.size() == m_volumes->size())
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        int object_idx = (*m_volumes)[i]->object_idx();
 | 
			
		||||
        if (object_idx >= 1000)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        // Process unselected volumes of the object.
 | 
			
		||||
        for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
 | 
			
		||||
        {
 | 
			
		||||
            if (done.size() == m_volumes->size())
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            if (done.find(j) != done.end())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            GLVolume* v = (*m_volumes)[j];
 | 
			
		||||
            if (v->object_idx() != object_idx)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            v->set_offset(v->get_offset() + displacement);
 | 
			
		||||
            done.insert(j);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_bounding_box_dirty = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement)
 | 
			
		||||
{
 | 
			
		||||
    if (!m_valid)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i : m_list)
 | 
			
		||||
    {
 | 
			
		||||
        GLVolume* v = (*m_volumes)[i];
 | 
			
		||||
        if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
 | 
			
		||||
            v->set_offset(v->get_offset() + displacement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::set<unsigned int> done;  // prevent processing volumes twice
 | 
			
		||||
    done.insert(m_list.begin(), m_list.end());
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i : m_list)
 | 
			
		||||
    {
 | 
			
		||||
        if (done.size() == m_volumes->size())
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        int object_idx = (*m_volumes)[i]->object_idx();
 | 
			
		||||
        if (object_idx >= 1000)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        // Process unselected volumes of the object.
 | 
			
		||||
        for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
 | 
			
		||||
        {
 | 
			
		||||
            if (done.size() == m_volumes->size())
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            if (done.find(j) != done.end())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            GLVolume* v = (*m_volumes)[j];
 | 
			
		||||
            if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            v->set_offset(v->get_offset() + displacement);
 | 
			
		||||
            done.insert(j);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_bounding_box_dirty = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::Selection::render(bool show_indirect_selection) const
 | 
			
		||||
{
 | 
			
		||||
    if (is_empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -6648,10 +6744,10 @@ void GLCanvas3D::_on_move()
 | 
			
		|||
        if (done.find(done_id) != done.end())
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        done.insert(done_id);
 | 
			
		||||
 | 
			
		||||
        if (object_idx < 1000)
 | 
			
		||||
        {
 | 
			
		||||
            done.insert(done_id);
 | 
			
		||||
 | 
			
		||||
            // Move instances.
 | 
			
		||||
            ModelObject* model_object = m_model->objects[object_idx];
 | 
			
		||||
            if (model_object != nullptr)
 | 
			
		||||
| 
						 | 
				
			
			@ -6666,6 +6762,14 @@ void GLCanvas3D::_on_move()
 | 
			
		|||
            wipe_tower_origin = v->get_offset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const std::pair<int, int>& i : done)
 | 
			
		||||
    {
 | 
			
		||||
        ModelObject* m = m_model->objects[i.first];
 | 
			
		||||
        Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
 | 
			
		||||
        m_selection.translate(i.first, i.second, shift);
 | 
			
		||||
        m->translate_instance(i.second, shift);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (object_moved)
 | 
			
		||||
        post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -6700,10 +6804,19 @@ void GLCanvas3D::_on_rotate()
 | 
			
		|||
        if (model_object != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            model_object->instances[instance_idx]->set_rotation(v->get_rotation());
 | 
			
		||||
            model_object->instances[instance_idx]->set_offset(v->get_offset());
 | 
			
		||||
            model_object->invalidate_bounding_box();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const std::pair<int, int>& i : done)
 | 
			
		||||
    {
 | 
			
		||||
        ModelObject* m = m_model->objects[i.first];
 | 
			
		||||
        Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
 | 
			
		||||
        m_selection.translate(i.first, i.second, shift);
 | 
			
		||||
        m->translate_instance(i.second, shift);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -6734,10 +6847,19 @@ void GLCanvas3D::_on_scale()
 | 
			
		|||
        if (model_object != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor());
 | 
			
		||||
            model_object->instances[instance_idx]->set_offset(v->get_offset());
 | 
			
		||||
            model_object->invalidate_bounding_box();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const std::pair<int, int>& i : done)
 | 
			
		||||
    {
 | 
			
		||||
        ModelObject* m = m_model->objects[i.first];
 | 
			
		||||
        Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
 | 
			
		||||
        m_selection.translate(i.first, i.second, shift);
 | 
			
		||||
        m->translate_instance(i.second, shift);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -508,6 +508,9 @@ public:
 | 
			
		|||
        void mirror(Axis axis);
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
 | 
			
		||||
        void translate(unsigned int object_idx, const Vec3d& displacement);
 | 
			
		||||
        void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
 | 
			
		||||
 | 
			
		||||
        void render(bool show_indirect_selection) const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -926,7 +926,11 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
 | 
			
		|||
 | 
			
		||||
        // gets transform from first selected volume
 | 
			
		||||
        const GLVolume* v = selection.get_volume(*idxs.begin());
 | 
			
		||||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
        transform = v->world_matrix();
 | 
			
		||||
#else
 | 
			
		||||
        transform = v->world_matrix().cast<double>();
 | 
			
		||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
 | 
			
		||||
        // gets angles from first selected volume
 | 
			
		||||
        angles = v->get_rotation();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -135,11 +135,11 @@ PreviewIface* create_preview_iface(wxNotebook* parent, DynamicPrintConfig* confi
 | 
			
		|||
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
 | 
			
		||||
{
 | 
			
		||||
	try{
 | 
			
		||||
		switch (config.def()->get(opt_key)->type){
 | 
			
		||||
		switch (config.def()->get(opt_key)->type) {
 | 
			
		||||
		case coFloatOrPercent:{
 | 
			
		||||
			std::string str = boost::any_cast<std::string>(value);
 | 
			
		||||
			bool percent = false;
 | 
			
		||||
			if (str.back() == '%'){
 | 
			
		||||
			if (str.back() == '%') {
 | 
			
		||||
				str.pop_back();
 | 
			
		||||
				percent = true;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +172,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 | 
			
		|||
				config.option<ConfigOptionStrings>(opt_key)->values = 
 | 
			
		||||
					boost::any_cast<std::vector<std::string>>(value);
 | 
			
		||||
			}
 | 
			
		||||
			else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){
 | 
			
		||||
			else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) {
 | 
			
		||||
				std::string str = boost::any_cast<std::string>(value);
 | 
			
		||||
				if (str.back() == ';') str.pop_back();
 | 
			
		||||
				// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
 | 
			
		||||
| 
						 | 
				
			
			@ -219,7 +219,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 | 
			
		|||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case coPoints:{
 | 
			
		||||
			if (opt_key.compare("bed_shape") == 0){
 | 
			
		||||
			if (opt_key.compare("bed_shape") == 0) {
 | 
			
		||||
				config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Vec2d>>(value);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -239,22 +239,26 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void show_error(wxWindow* parent, const wxString& message) {
 | 
			
		||||
void show_error(wxWindow* parent, const wxString& message)
 | 
			
		||||
{
 | 
			
		||||
	ErrorDialog msg(parent, message);
 | 
			
		||||
	msg.ShowModal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void show_error_id(int id, const std::string& message) {
 | 
			
		||||
void show_error_id(int id, const std::string& message)
 | 
			
		||||
{
 | 
			
		||||
	auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr;
 | 
			
		||||
	show_error(parent, wxString::FromUTF8(message.data()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void show_info(wxWindow* parent, const wxString& message, const wxString& title){
 | 
			
		||||
void show_info(wxWindow* parent, const wxString& message, const wxString& title)
 | 
			
		||||
{
 | 
			
		||||
	wxMessageDialog msg_wingow(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
 | 
			
		||||
	msg_wingow.ShowModal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void warning_catcher(wxWindow* parent, const wxString& message){
 | 
			
		||||
void warning_catcher(wxWindow* parent, const wxString& message)
 | 
			
		||||
{
 | 
			
		||||
	if (message == "GLUquadricObjPtr | " + _(L("Attempt to free unreferenced scalar")) )
 | 
			
		||||
		return;
 | 
			
		||||
	wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,7 +122,7 @@ bool GUI_App::OnInit()
 | 
			
		|||
        // try to get the mutex. If we can't, just skip this idle event and get the next one.
 | 
			
		||||
        if (!callback_register.try_lock()) return;
 | 
			
		||||
        // pop callback
 | 
			
		||||
        if (m_cb.size() != 0){
 | 
			
		||||
        if (m_cb.size() != 0) {
 | 
			
		||||
            cur_cb = m_cb.top();
 | 
			
		||||
            m_cb.pop();
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +143,7 @@ bool GUI_App::OnInit()
 | 
			
		|||
    // On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...)
 | 
			
		||||
    // are shown before or in the same event callback with the main frame creation.
 | 
			
		||||
    // Therefore we schedule them for later using CallAfter.
 | 
			
		||||
    CallAfter([this](){
 | 
			
		||||
    CallAfter([this]() {
 | 
			
		||||
        //         eval{
 | 
			
		||||
        if (!preset_updater->config_update())
 | 
			
		||||
            mainframe->Close();
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +154,7 @@ bool GUI_App::OnInit()
 | 
			
		|||
        //         }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    CallAfter([this](){
 | 
			
		||||
    CallAfter([this]() {
 | 
			
		||||
        if (!config_wizard_startup(app_conf_exists)) {
 | 
			
		||||
            // Only notify if there was not wizard so as not to bother too much ...
 | 
			
		||||
            preset_updater->slic3r_update_notify();
 | 
			
		||||
| 
						 | 
				
			
			@ -196,13 +196,13 @@ void GUI_App::init_label_colours()
 | 
			
		|||
 | 
			
		||||
void GUI_App::update_label_colours_from_appconfig()
 | 
			
		||||
{
 | 
			
		||||
    if (app_config->has("label_clr_sys")){
 | 
			
		||||
    if (app_config->has("label_clr_sys")) {
 | 
			
		||||
        auto str = app_config->get("label_clr_sys");
 | 
			
		||||
        if (str != "")
 | 
			
		||||
            m_color_label_sys = wxColour(str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (app_config->has("label_clr_modified")){
 | 
			
		||||
    if (app_config->has("label_clr_modified")) {
 | 
			
		||||
        auto str = app_config->get("label_clr_modified");
 | 
			
		||||
        if (str != "")
 | 
			
		||||
            m_color_label_modified = wxColour(str);
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +251,7 @@ void GUI_App::recreate_GUI()
 | 
			
		|||
 | 
			
		||||
    // On OSX the UI was not initialized correctly if the wizard was called
 | 
			
		||||
    // before the UI was up and running.
 | 
			
		||||
    CallAfter([](){
 | 
			
		||||
    CallAfter([]() {
 | 
			
		||||
        // Run the config wizard, don't offer the "reset user profile" checkbox.
 | 
			
		||||
        config_wizard_startup(true);
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +267,8 @@ void GUI_App::system_info()
 | 
			
		|||
// static method accepting a wxWindow object as first parameter
 | 
			
		||||
bool GUI_App::catch_error(std::function<void()> cb,
 | 
			
		||||
    //                       wxMessageDialog* message_dialog,
 | 
			
		||||
    const std::string& err /*= ""*/){
 | 
			
		||||
    const std::string& err /*= ""*/)
 | 
			
		||||
{
 | 
			
		||||
    if (!err.empty()) {
 | 
			
		||||
        if (cb)
 | 
			
		||||
            cb();
 | 
			
		||||
| 
						 | 
				
			
			@ -280,14 +281,16 @@ bool GUI_App::catch_error(std::function<void()> cb,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// static method accepting a wxWindow object as first parameter
 | 
			
		||||
void fatal_error(wxWindow* parent){
 | 
			
		||||
void fatal_error(wxWindow* parent)
 | 
			
		||||
{
 | 
			
		||||
    show_error(parent, "");
 | 
			
		||||
    //     exit 1; // #ys_FIXME
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Called after the Preferences dialog is closed and the program settings are saved.
 | 
			
		||||
// Update the UI based on the current preferences.
 | 
			
		||||
void GUI_App::update_ui_from_settings(){
 | 
			
		||||
void GUI_App::update_ui_from_settings()
 | 
			
		||||
{
 | 
			
		||||
    mainframe->update_ui_from_settings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -364,7 +367,7 @@ bool GUI_App::select_language(  wxArrayString & names,
 | 
			
		|||
        _(L("Array of language names and identifiers should have the same size.")));
 | 
			
		||||
    int init_selection = 0;
 | 
			
		||||
    long current_language = m_wxLocale ? m_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN;
 | 
			
		||||
    for (auto lang : identifiers){
 | 
			
		||||
    for (auto lang : identifiers) {
 | 
			
		||||
        if (lang == current_language)
 | 
			
		||||
            break;
 | 
			
		||||
        ++init_selection;
 | 
			
		||||
| 
						 | 
				
			
			@ -526,7 +529,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
 | 
			
		|||
    // TODO: for when we're able to flash dictionaries
 | 
			
		||||
    // local_menu->Append(config_id_base + FirmwareMenuDict,  _(L("Flash language file")),    _(L("Upload a language dictionary file into a Prusa printer")));
 | 
			
		||||
 | 
			
		||||
    local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event){
 | 
			
		||||
    local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) {
 | 
			
		||||
        switch (event.GetId() - config_id_base) {
 | 
			
		||||
        case ConfigMenuWizard:
 | 
			
		||||
            config_wizard(ConfigWizard::RR_USER);
 | 
			
		||||
| 
						 | 
				
			
			@ -692,7 +695,7 @@ wxNotebook* GUI_App::tab_panel() const
 | 
			
		|||
// }
 | 
			
		||||
 | 
			
		||||
// Do we need this function???
 | 
			
		||||
// void GUI_App::notify(message){
 | 
			
		||||
// void GUI_App::notify(message) {
 | 
			
		||||
//     auto frame = GetTopWindow();
 | 
			
		||||
//     // try harder to attract user attention on OS X
 | 
			
		||||
//     if (!frame->IsActive())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -150,7 +150,8 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
 | 
			
		|||
        GetMainWindow()->SetToolTip(""); // hide tooltip
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxPoint ObjectList::get_mouse_position_in_control() {
 | 
			
		||||
wxPoint ObjectList::get_mouse_position_in_control()
 | 
			
		||||
{
 | 
			
		||||
    const wxPoint& pt = wxGetMousePosition();
 | 
			
		||||
//     wxWindow* win = GetMainWindow();
 | 
			
		||||
//     wxPoint screen_pos = win->GetScreenPosition();
 | 
			
		||||
| 
						 | 
				
			
			@ -159,10 +160,9 @@ wxPoint ObjectList::get_mouse_position_in_control() {
 | 
			
		|||
 | 
			
		||||
int ObjectList::get_selected_obj_idx() const
 | 
			
		||||
{
 | 
			
		||||
    if (GetSelectedItemsCount() == 1) {
 | 
			
		||||
        auto item = GetSelection();
 | 
			
		||||
        return m_objects_model->GetIdByItem(item);
 | 
			
		||||
    }
 | 
			
		||||
    if (GetSelectedItemsCount() == 1)
 | 
			
		||||
        return m_objects_model->GetIdByItem(m_objects_model->GetTopParent(GetSelection()));
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -209,7 +209,8 @@ void ObjectList::update_extruder_in_config(const wxString& selection)
 | 
			
		|||
    wxGetApp().plater()->update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::init_icons(){
 | 
			
		||||
void ObjectList::init_icons()
 | 
			
		||||
{
 | 
			
		||||
    m_bmp_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
    m_bmp_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -297,7 +298,7 @@ void ObjectList::key_event(wxKeyEvent& event)
 | 
			
		|||
#ifdef __WXOSX__
 | 
			
		||||
        || event.GetKeyCode() == WXK_BACK
 | 
			
		||||
#endif //__WXOSX__
 | 
			
		||||
        ){
 | 
			
		||||
        ) {
 | 
			
		||||
        printf("WXK_BACK\n");
 | 
			
		||||
        remove();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -565,7 +566,7 @@ wxMenu* ObjectList::create_add_part_popupmenu()
 | 
			
		|||
    // Append settings popupmenu
 | 
			
		||||
    menu->Append(menu_item_settings(menu, config_id_base + i + 5, false));
 | 
			
		||||
 | 
			
		||||
    menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event){
 | 
			
		||||
    menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) {
 | 
			
		||||
        switch (event.GetId() - config_id_base) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            load_subobject();
 | 
			
		||||
| 
						 | 
				
			
			@ -609,7 +610,7 @@ wxMenu* ObjectList::create_part_settings_popupmenu()
 | 
			
		|||
    // Append settings popupmenu
 | 
			
		||||
    menu->Append(menu_item_settings(menu, config_id_base + 1, true));
 | 
			
		||||
 | 
			
		||||
    menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event){
 | 
			
		||||
    menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) {
 | 
			
		||||
        switch (event.GetId() - config_id_base) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            split(true);
 | 
			
		||||
| 
						 | 
				
			
			@ -686,6 +687,7 @@ void ObjectList::load_part( ModelObject* model_object,
 | 
			
		|||
{
 | 
			
		||||
    wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
 | 
			
		||||
 | 
			
		||||
    m_parts_changed = false;
 | 
			
		||||
    wxArrayString input_files;
 | 
			
		||||
    wxGetApp().open_model(parent, input_files);
 | 
			
		||||
    for (int i = 0; i < input_files.size(); ++i) {
 | 
			
		||||
| 
						 | 
				
			
			@ -706,6 +708,7 @@ void ObjectList::load_part( ModelObject* model_object,
 | 
			
		|||
            if (model_object->origin_translation != Vec3d::Zero())
 | 
			
		||||
            {
 | 
			
		||||
                object->center_around_origin();
 | 
			
		||||
                object->ensure_on_bed();
 | 
			
		||||
                delta = model_object->origin_translation - object->origin_translation;
 | 
			
		||||
            }
 | 
			
		||||
            for (auto volume : object->volumes) {
 | 
			
		||||
| 
						 | 
				
			
			@ -737,6 +740,7 @@ void ObjectList::load_lambda(   ModelObject* model_object,
 | 
			
		|||
{
 | 
			
		||||
    auto dlg = new LambdaObjectDialog(GetMainWindow());
 | 
			
		||||
    if (dlg->ShowModal() == wxID_CANCEL) {
 | 
			
		||||
        m_parts_changed = false;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -800,7 +804,7 @@ void ObjectList::load_lambda(const std::string& type_name)
 | 
			
		|||
        mesh = make_cylinder(params.cyl_r, params.cyl_h);
 | 
			
		||||
    else if (type_name == _("Sphere"))
 | 
			
		||||
        mesh = make_sphere(params.sph_rho);
 | 
			
		||||
    else if (type_name == _("Slab")){
 | 
			
		||||
    else if (type_name == _("Slab")) {
 | 
			
		||||
        const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size();
 | 
			
		||||
        mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h);
 | 
			
		||||
        // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
 | 
			
		||||
| 
						 | 
				
			
			@ -955,6 +959,11 @@ void ObjectList::split(const bool split_part)
 | 
			
		|||
 | 
			
		||||
    m_parts_changed = true;
 | 
			
		||||
    parts_changed(m_selected_object_id);
 | 
			
		||||
 | 
			
		||||
#if ENABLE_EXTENDED_SELECTION
 | 
			
		||||
    // restores selection
 | 
			
		||||
    _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection().add_object(m_selected_object_id);
 | 
			
		||||
#endif // ENABLE_EXTENDED_SELECTION
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume)
 | 
			
		||||
| 
						 | 
				
			
			@ -995,9 +1004,17 @@ bool ObjectList::is_splittable_object(const bool split_part)
 | 
			
		|||
    return splittable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::part_settings_changed()
 | 
			
		||||
{
 | 
			
		||||
    m_part_settings_changed = true;
 | 
			
		||||
    wxGetApp().plater()->changed_object(get_selected_obj_idx());
 | 
			
		||||
    m_part_settings_changed = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::parts_changed(int obj_idx)
 | 
			
		||||
{
 | 
			
		||||
    wxGetApp().mainframe->m_plater->changed_object_settings(obj_idx);
 | 
			
		||||
    wxGetApp().plater()->changed_object(get_selected_obj_idx());
 | 
			
		||||
    m_parts_changed = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::part_selection_changed()
 | 
			
		||||
| 
						 | 
				
			
			@ -1035,7 +1052,7 @@ void ObjectList::part_selection_changed()
 | 
			
		|||
                    m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (m_objects_model->GetItemType(item) == itVolume){
 | 
			
		||||
            else if (m_objects_model->GetItemType(item) == itVolume) {
 | 
			
		||||
                og_name = _(L("Part manipulation"));
 | 
			
		||||
                is_part = true;
 | 
			
		||||
                const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
 | 
			
		||||
| 
						 | 
				
			
			@ -1085,7 +1102,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
 | 
			
		|||
    auto stats = model_object->volumes[0]->mesh.stl.stats;
 | 
			
		||||
    int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
 | 
			
		||||
        stats.facets_added + stats.facets_reversed + stats.backwards_edges;
 | 
			
		||||
    if (errors > 0)		{
 | 
			
		||||
    if (errors > 0) {
 | 
			
		||||
        wxVariant variant;
 | 
			
		||||
        variant << PrusaDataViewBitmapText(item_name, m_bmp_manifold_warning);
 | 
			
		||||
        m_objects_model->SetValue(variant, item, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -1235,7 +1252,7 @@ void ObjectList::update_selections_on_canvas()
 | 
			
		|||
 | 
			
		||||
    auto add_to_selection = [this](const wxDataViewItem& item, GLCanvas3D::Selection& selection, bool as_single_selection)
 | 
			
		||||
    {        
 | 
			
		||||
        if (m_objects_model->GetParent(item) == wxDataViewItem(0)){
 | 
			
		||||
        if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
 | 
			
		||||
            selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,10 +95,11 @@ public:
 | 
			
		|||
    bool                is_splittable_object(const bool split_part);
 | 
			
		||||
 | 
			
		||||
    wxPoint             get_mouse_position_in_control();
 | 
			
		||||
    wxBoxSizer*         get_sizer(){return  m_sizer;}
 | 
			
		||||
    wxBoxSizer*         get_sizer() {return  m_sizer;}
 | 
			
		||||
    int                 get_selected_obj_idx() const;
 | 
			
		||||
    bool                is_parts_changed() const        { return m_parts_changed; }
 | 
			
		||||
    bool                is_part_settings_changed() const{ return m_part_settings_changed; }
 | 
			
		||||
    bool                is_parts_changed() const { return m_parts_changed; }
 | 
			
		||||
    bool                is_part_settings_changed() const { return m_part_settings_changed; }
 | 
			
		||||
    void                part_settings_changed();
 | 
			
		||||
 | 
			
		||||
    void                 parts_changed(int obj_idx);
 | 
			
		||||
    void                 part_selection_changed();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,8 +32,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent):
 | 
			
		|||
    m_og->label_width = 100;
 | 
			
		||||
    m_og->set_grid_vgap(5);
 | 
			
		||||
 | 
			
		||||
    m_og->m_on_change = [this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
        if (opt_key == "scale_unit"){
 | 
			
		||||
    m_og->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
        if (opt_key == "scale_unit") {
 | 
			
		||||
            const wxString& selection = boost::any_cast<wxString>(value);
 | 
			
		||||
            std::vector<std::string> axes{ "x", "y", "z" };
 | 
			
		||||
            for (auto axis : axes) {
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent):
 | 
			
		|||
        if (option_name == "Scale") {
 | 
			
		||||
            line.near_label_widget = [](wxWindow* parent) {
 | 
			
		||||
                auto btn = new PrusaLockButton(parent, wxID_ANY);
 | 
			
		||||
                btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event){
 | 
			
		||||
                btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event) {
 | 
			
		||||
                    event.Skip();
 | 
			
		||||
                    wxTheApp->CallAfter([btn]() {
 | 
			
		||||
                        wxGetApp().obj_manipul()->set_uniform_scaling(btn->IsLocked());
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +188,7 @@ void ObjectManipulation::update_settings_list()
 | 
			
		|||
#ifdef __WXMSW__
 | 
			
		||||
            btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
 | 
			
		||||
#endif // __WXMSW__
 | 
			
		||||
			btn->Bind(wxEVT_BUTTON, [opt_key, config](wxEvent &event){
 | 
			
		||||
			btn->Bind(wxEVT_BUTTON, [opt_key, config](wxEvent &event) {
 | 
			
		||||
				config->erase(opt_key);
 | 
			
		||||
                wxTheApp->CallAfter([]() { wxGetApp().obj_manipul()->update_settings_list(); });
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			@ -225,6 +225,9 @@ void ObjectManipulation::update_settings_list()
 | 
			
		|||
                optgroup->label_width = 150;
 | 
			
		||||
                optgroup->sidetext_width = 70;
 | 
			
		||||
 | 
			
		||||
                optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
 | 
			
		||||
                                        wxGetApp().obj_list()->part_settings_changed(); };
 | 
			
		||||
 | 
			
		||||
                for (auto& opt : cat.second)
 | 
			
		||||
                {
 | 
			
		||||
                    if (opt == "extruder")
 | 
			
		||||
| 
						 | 
				
			
			@ -268,7 +271,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
 | 
			
		|||
{
 | 
			
		||||
    if (selection.is_single_full_object())
 | 
			
		||||
    {
 | 
			
		||||
        if (wxGetApp().mainframe->m_plater->model().objects[selection.get_object_idx()]->instances.size() == 1)
 | 
			
		||||
        if (!wxGetApp().model_objects()->empty() && (*wxGetApp().model_objects())[selection.get_object_idx()]->instances.size() == 1)
 | 
			
		||||
        {
 | 
			
		||||
            // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
 | 
			
		||||
            const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,8 +38,8 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
 | 
			
		|||
    ConfigOptionDef def;
 | 
			
		||||
    def.width = 70;
 | 
			
		||||
	auto optgroup = init_modificator_options_page(_(L("Box")));
 | 
			
		||||
    if (optgroup){
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
    if (optgroup) {
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
			int opt_id =	opt_key == "l" ? 0 :
 | 
			
		||||
							opt_key == "w" ? 1 : 
 | 
			
		||||
							opt_key == "h" ? 2 : -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,8 +63,8 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	optgroup = init_modificator_options_page(_(L("Cylinder")));
 | 
			
		||||
	if (optgroup){
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
	if (optgroup) {
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
			int val = boost::any_cast<int>(value);
 | 
			
		||||
			if (opt_key == "cyl_r")
 | 
			
		||||
				object_parameters.cyl_r = val;
 | 
			
		||||
| 
						 | 
				
			
			@ -85,8 +85,8 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
	optgroup = init_modificator_options_page(_(L("Sphere")));
 | 
			
		||||
	if (optgroup){
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
	if (optgroup) {
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
			if (opt_key == "sph_rho")
 | 
			
		||||
				object_parameters.sph_rho = boost::any_cast<double>(value);
 | 
			
		||||
			else return;
 | 
			
		||||
| 
						 | 
				
			
			@ -100,8 +100,8 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	optgroup = init_modificator_options_page(_(L("Slab")));
 | 
			
		||||
	if (optgroup){
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
	if (optgroup) {
 | 
			
		||||
		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
			double val = boost::any_cast<double>(value);
 | 
			
		||||
			if (opt_key == "slab_z")
 | 
			
		||||
				object_parameters.slab_z = val;
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +171,8 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
 | 
			
		|||
 | 
			
		||||
// Called from the constructor.
 | 
			
		||||
// Create a panel for a rectangular / circular / custom bed shape.
 | 
			
		||||
ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title){
 | 
			
		||||
ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title)
 | 
			
		||||
{
 | 
			
		||||
    if (!m_type_name.IsEmpty() && m_type_name != title)
 | 
			
		||||
        return nullptr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,10 +41,10 @@ class LambdaObjectDialog : public wxDialog
 | 
			
		|||
public:
 | 
			
		||||
    LambdaObjectDialog(wxWindow* parent, 
 | 
			
		||||
                       const wxString type_name = wxEmptyString);
 | 
			
		||||
	~LambdaObjectDialog(){}
 | 
			
		||||
	~LambdaObjectDialog() {}
 | 
			
		||||
 | 
			
		||||
	bool CanClose() { return true; }	// ???
 | 
			
		||||
	OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; }
 | 
			
		||||
	OBJECT_PARAMETERS& ObjectParameters() { return object_parameters; }
 | 
			
		||||
 | 
			
		||||
	ConfigOptionsGroupShp init_modificator_options_page(const wxString& title);
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,7 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
 | 
			
		|||
    Layout();
 | 
			
		||||
 | 
			
		||||
    // declare events
 | 
			
		||||
    Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event){
 | 
			
		||||
    Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
 | 
			
		||||
        if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
 | 
			
		||||
            event.Veto();
 | 
			
		||||
            return;
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ void MainFrame::init_tabpanel()
 | 
			
		|||
{
 | 
			
		||||
    m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
 | 
			
		||||
 | 
			
		||||
    m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&){
 | 
			
		||||
    m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) {
 | 
			
		||||
        auto panel = m_tabpanel->GetCurrentPage();
 | 
			
		||||
//             panel->OnActivate(); if panel->can('OnActivate');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +162,7 @@ void MainFrame::init_tabpanel()
 | 
			
		|||
 | 
			
		||||
        // Show a correct number of filament fields.
 | 
			
		||||
        // nozzle_diameter is undefined when SLA printer is selected
 | 
			
		||||
        if (full_config.has("nozzle_diameter")){
 | 
			
		||||
        if (full_config.has("nozzle_diameter")) {
 | 
			
		||||
            m_plater->on_extruders_change(full_config.option<ConfigOptionFloats>("nozzle_diameter")->values.size());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +181,7 @@ std::vector<PresetTab>& MainFrame::get_preset_tabs() {
 | 
			
		|||
Tab* MainFrame::get_tab(const std::string& name)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<PresetTab>::iterator it = std::find_if(preset_tabs.begin(), preset_tabs.end(),
 | 
			
		||||
        [name](PresetTab& tab){ return name == tab.name; });
 | 
			
		||||
        [name](PresetTab& tab) { return name == tab.name; });
 | 
			
		||||
    return it != preset_tabs.end() ? it->panel : nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +218,7 @@ void MainFrame::add_created_tab(Tab* panel)
 | 
			
		|||
    bool add_panel = true;
 | 
			
		||||
 | 
			
		||||
    auto it = std::find_if(preset_tabs.begin(), preset_tabs.end(),
 | 
			
		||||
        [tab_name](PresetTab& tab){return tab.name == tab_name; });
 | 
			
		||||
        [tab_name](PresetTab& tab) {return tab.name == tab_name; });
 | 
			
		||||
    if (it != preset_tabs.end()) {
 | 
			
		||||
        it->panel = panel;
 | 
			
		||||
        add_panel = it->technology == wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
 | 
			
		||||
| 
						 | 
				
			
			@ -236,59 +236,59 @@ void MainFrame::init_menubar()
 | 
			
		|||
        append_menu_item(fileMenu, wxID_ANY, _(L("Open STL/OBJ/AMF/3MF…\tCtrl+O")), _(L("Open a model")),
 | 
			
		||||
                        [this](wxCommandEvent&) { if (m_plater) m_plater->add(); }, "brick_add.png");
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("&Load Config…\tCtrl+L")), _(L("Load exported configuration file")), 
 | 
			
		||||
                        [this](wxCommandEvent&){ load_config_file(); }, "plugin_add.png");
 | 
			
		||||
                        [this](wxCommandEvent&) { load_config_file(); }, "plugin_add.png");
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("&Export Config…\tCtrl+E")), _(L("Export current configuration to file")), 
 | 
			
		||||
                        [this](wxCommandEvent&){ export_config(); }, "plugin_go.png");
 | 
			
		||||
                        [this](wxCommandEvent&) { export_config(); }, "plugin_go.png");
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("&Load Config Bundle…")), _(L("Load presets from a bundle")), 
 | 
			
		||||
                        [this](wxCommandEvent&){ load_configbundle(); }, "lorry_add.png");
 | 
			
		||||
                        [this](wxCommandEvent&) { load_configbundle(); }, "lorry_add.png");
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("&Export Config Bundle…")), _(L("Export all presets to file")), 
 | 
			
		||||
                        [this](wxCommandEvent&){ export_configbundle(); }, "lorry_go.png");
 | 
			
		||||
                        [this](wxCommandEvent&) { export_configbundle(); }, "lorry_go.png");
 | 
			
		||||
        fileMenu->AppendSeparator();
 | 
			
		||||
        wxMenuItem* repeat = nullptr;
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("Q&uick Slice…\tCtrl+U")), _(L("Slice a file into a G-code")), 
 | 
			
		||||
            [this, repeat](wxCommandEvent&){
 | 
			
		||||
                wxTheApp->CallAfter([this, repeat](){
 | 
			
		||||
            [this, repeat](wxCommandEvent&) {
 | 
			
		||||
                wxTheApp->CallAfter([this, repeat]() {
 | 
			
		||||
                    quick_slice();
 | 
			
		||||
                    repeat->Enable(is_last_input_file());
 | 
			
		||||
            }); }, "cog_go.png");
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice and Save &As…\tCtrl+Alt+U")), _(L("Slice a file into a G-code, save as")), 
 | 
			
		||||
            [this, repeat](wxCommandEvent&){
 | 
			
		||||
                wxTheApp->CallAfter([this, repeat](){
 | 
			
		||||
            [this, repeat](wxCommandEvent&) {
 | 
			
		||||
                wxTheApp->CallAfter([this, repeat]() {
 | 
			
		||||
                    quick_slice(qsSaveAs);
 | 
			
		||||
                    repeat->Enable(is_last_input_file());
 | 
			
		||||
            }); }, "cog_go.png");
 | 
			
		||||
        repeat = append_menu_item(fileMenu, wxID_ANY, _(L("&Repeat Last Quick Slice\tCtrl+Shift+U")), _(L("Repeat last quick slice")), 
 | 
			
		||||
            [this](wxCommandEvent&){
 | 
			
		||||
            wxTheApp->CallAfter([this](){
 | 
			
		||||
            [this](wxCommandEvent&) {
 | 
			
		||||
            wxTheApp->CallAfter([this]() {
 | 
			
		||||
                quick_slice(qsReslice);
 | 
			
		||||
            }); }, "cog_go.png");
 | 
			
		||||
        repeat->Enable(0);
 | 
			
		||||
        fileMenu->AppendSeparator();
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("Slice to SV&G…\tCtrl+G")), _(L("Slice file to a multi-layer SVG")),
 | 
			
		||||
            [this](wxCommandEvent&){ quick_slice(qsSaveAs | qsExportSVG); }, "shape_handles.png");
 | 
			
		||||
            [this](wxCommandEvent&) { quick_slice(qsSaveAs | qsExportSVG); }, "shape_handles.png");
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("Slice to PNG…")), _(L("Slice file to a set of PNG files")),
 | 
			
		||||
            [this](wxCommandEvent&){ slice_to_png(); /*$self->quick_slice(save_as = > 0, export_png = > 1);*/ }, "shape_handles.png");
 | 
			
		||||
            [this](wxCommandEvent&) { slice_to_png(); /*$self->quick_slice(save_as = > 0, export_png = > 1);*/ }, "shape_handles.png");
 | 
			
		||||
        m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(&Re)Slice Now\tCtrl+S")), _(L("Start new slicing process")),
 | 
			
		||||
            [this](wxCommandEvent&){ reslice_now(); }, "shape_handles.png");
 | 
			
		||||
            [this](wxCommandEvent&) { reslice_now(); }, "shape_handles.png");
 | 
			
		||||
        fileMenu->AppendSeparator();
 | 
			
		||||
        append_menu_item(fileMenu, wxID_ANY, _(L("Repair STL file…")), _(L("Automatically repair an STL file")),
 | 
			
		||||
            [this](wxCommandEvent&){ repair_stl(); }, "wrench.png");
 | 
			
		||||
            [this](wxCommandEvent&) { repair_stl(); }, "wrench.png");
 | 
			
		||||
        fileMenu->AppendSeparator();
 | 
			
		||||
        append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), _(L("Quit Slic3r")),
 | 
			
		||||
            [this](wxCommandEvent&){ Close(false); } );
 | 
			
		||||
            [this](wxCommandEvent&) { Close(false); } );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Plater menu
 | 
			
		||||
    if (m_plater) {
 | 
			
		||||
        m_plater_menu = new wxMenu();
 | 
			
		||||
        append_menu_item(m_plater_menu, wxID_ANY, _(L("Export G-code...")), _(L("Export current plate as G-code")),
 | 
			
		||||
            [this](wxCommandEvent&){ m_plater->export_gcode(); }, "cog_go.png");
 | 
			
		||||
            [this](wxCommandEvent&) { m_plater->export_gcode(); }, "cog_go.png");
 | 
			
		||||
        append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as STL...")), _(L("Export current plate as STL")),
 | 
			
		||||
            [this](wxCommandEvent&){ m_plater->export_stl(); }, "brick_go.png");
 | 
			
		||||
            [this](wxCommandEvent&) { m_plater->export_stl(); }, "brick_go.png");
 | 
			
		||||
        append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as AMF...")), _(L("Export current plate as AMF")),
 | 
			
		||||
            [this](wxCommandEvent&){ m_plater->export_amf(); }, "brick_go.png");
 | 
			
		||||
            [this](wxCommandEvent&) { m_plater->export_amf(); }, "brick_go.png");
 | 
			
		||||
        append_menu_item(m_plater_menu, wxID_ANY, _(L("Export plate as 3MF...")), _(L("Export current plate as 3MF")),
 | 
			
		||||
            [this](wxCommandEvent&){ m_plater->export_3mf(); }, "brick_go.png");
 | 
			
		||||
            [this](wxCommandEvent&) { m_plater->export_3mf(); }, "brick_go.png");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Window menu
 | 
			
		||||
| 
						 | 
				
			
			@ -297,18 +297,18 @@ void MainFrame::init_menubar()
 | 
			
		|||
        size_t tab_offset = 0;
 | 
			
		||||
        if (m_plater) {
 | 
			
		||||
            append_menu_item(windowMenu, wxID_ANY, L("Select &Plater Tab\tCtrl+1"), L("Show the plater"), 
 | 
			
		||||
                [this](wxCommandEvent&){ select_tab(0); }, "application_view_tile.png");
 | 
			
		||||
                [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
 | 
			
		||||
            tab_offset += 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (tab_offset > 0) {
 | 
			
		||||
            windowMenu->AppendSeparator();
 | 
			
		||||
        }
 | 
			
		||||
        append_menu_item(windowMenu, wxID_ANY, L("Select P&rint Settings Tab\tCtrl+2"), L("Show the print settings"), 
 | 
			
		||||
            [this, tab_offset](wxCommandEvent&){ select_tab(tab_offset + 0); }, "cog.png");
 | 
			
		||||
            [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
 | 
			
		||||
        append_menu_item(windowMenu, wxID_ANY, L("Select &Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), 
 | 
			
		||||
            [this, tab_offset](wxCommandEvent&){ select_tab(tab_offset + 1); }, "spool.png");
 | 
			
		||||
            [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png");
 | 
			
		||||
        append_menu_item(windowMenu, wxID_ANY, L("Select Print&er Settings Tab\tCtrl+4"), L("Show the printer settings"), 
 | 
			
		||||
            [this, tab_offset](wxCommandEvent&){ select_tab(tab_offset + 2); }, "printer_empty.png");
 | 
			
		||||
            [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // View menu
 | 
			
		||||
| 
						 | 
				
			
			@ -317,40 +317,40 @@ void MainFrame::init_menubar()
 | 
			
		|||
        // \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators,
 | 
			
		||||
        // as the simple numeric accelerators spoil all numeric data entry.
 | 
			
		||||
        // The camera control accelerators are captured by GLCanvas3D::on_char().
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&){ select_view("iso"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); });
 | 
			
		||||
        m_viewMenu->AppendSeparator();
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&){ select_view("top"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&){ select_view("bottom"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&){ select_view("front"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Rear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&){ select_view("rear"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&){ select_view("left"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&){ select_view("right"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Rear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); });
 | 
			
		||||
        append_menu_item(m_viewMenu, wxID_ANY, _(L("Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Help menu
 | 
			
		||||
    auto helpMenu = new wxMenu();
 | 
			
		||||
    {
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("Prusa 3D Drivers")), _(L("Open the Prusa3D drivers download page in your browser")), 
 | 
			
		||||
            [this](wxCommandEvent&){ wxLaunchDefaultBrowser("http://www.prusa3d.com/drivers/"); });
 | 
			
		||||
            [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://www.prusa3d.com/drivers/"); });
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("Prusa Edition Releases")), _(L("Open the Prusa Edition releases page in your browser")), 
 | 
			
		||||
            [this](wxCommandEvent&){ wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/releases"); });
 | 
			
		||||
            [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/releases"); });
 | 
			
		||||
//#        my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
 | 
			
		||||
//#            wxTheApp->check_version(1);
 | 
			
		||||
//#        });
 | 
			
		||||
//#        $versioncheck->Enable(wxTheApp->have_version_check);
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r &Website")), _(L("Open the Slic3r website in your browser")), 
 | 
			
		||||
            [this](wxCommandEvent&){ wxLaunchDefaultBrowser("http://slic3r.org/"); });
 | 
			
		||||
            [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://slic3r.org/"); });
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r &Manual")), _(L("Open the Slic3r manual in your browser")), 
 | 
			
		||||
            [this](wxCommandEvent&){ wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
 | 
			
		||||
            [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
 | 
			
		||||
        helpMenu->AppendSeparator();
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("System Info")), _(L("Show system information")), 
 | 
			
		||||
            [this](wxCommandEvent&){ wxGetApp().system_info(); });
 | 
			
		||||
            [this](wxCommandEvent&) { wxGetApp().system_info(); });
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("Show &Configuration Folder")), _(L("Show user configuration folder (datadir)")), 
 | 
			
		||||
            [this](wxCommandEvent&){ Slic3r::GUI::desktop_open_datadir_folder(); });
 | 
			
		||||
            [this](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("Report an Issue")), _(L("Report an issue on the Slic3r Prusa Edition")), 
 | 
			
		||||
            [this](wxCommandEvent&){ wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); });
 | 
			
		||||
            [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); });
 | 
			
		||||
        append_menu_item(helpMenu, wxID_ANY, _(L("&About Slic3r")), _(L("Show about dialog")), 
 | 
			
		||||
            [this](wxCommandEvent&){ Slic3r::GUI::about(); });
 | 
			
		||||
            [this](wxCommandEvent&) { Slic3r::GUI::about(); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // menubar
 | 
			
		||||
| 
						 | 
				
			
			@ -369,14 +369,16 @@ void MainFrame::init_menubar()
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainFrame::slice_to_png(){
 | 
			
		||||
void MainFrame::slice_to_png()
 | 
			
		||||
{
 | 
			
		||||
//     m_plater->stop_background_process();
 | 
			
		||||
//     m_plater->async_apply_config();
 | 
			
		||||
    m_appController->print_ctl()->slice_to_png();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
 | 
			
		||||
void MainFrame::quick_slice(const int qs){
 | 
			
		||||
void MainFrame::quick_slice(const int qs)
 | 
			
		||||
{
 | 
			
		||||
//     my $progress_dialog;
 | 
			
		||||
    wxString input_file;
 | 
			
		||||
//  eval
 | 
			
		||||
| 
						 | 
				
			
			@ -422,7 +424,7 @@ void MainFrame::quick_slice(const int qs){
 | 
			
		|||
// 
 | 
			
		||||
//     auto sprint = new Slic3r::Print::Simple(
 | 
			
		||||
//         print_center = > print_center,
 | 
			
		||||
//         status_cb = > [](int percent, const wxString& msg){
 | 
			
		||||
//         status_cb = > [](int percent, const wxString& msg) {
 | 
			
		||||
//         m_progress_dialog->Update(percent, msg+"…");
 | 
			
		||||
//     });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -502,7 +504,7 @@ void MainFrame::quick_slice(const int qs){
 | 
			
		|||
//     wxTheApp->notify(message);
 | 
			
		||||
    wxMessageDialog(this, message, _(L("Slicing Done!")), wxOK | wxICON_INFORMATION).ShowModal();
 | 
			
		||||
//     };
 | 
			
		||||
//     Slic3r::GUI::catch_error(this, [](){ if (m_progress_dialog) m_progress_dialog->Destroy(); });
 | 
			
		||||
//     Slic3r::GUI::catch_error(this, []() { if (m_progress_dialog) m_progress_dialog->Destroy(); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainFrame::reslice_now()
 | 
			
		||||
| 
						 | 
				
			
			@ -701,7 +703,7 @@ void MainFrame::on_presets_changed(SimpleEvent &event)
 | 
			
		|||
 | 
			
		||||
        // FIXME: The preset type really should be a property of Tab instead
 | 
			
		||||
        Slic3r::Preset::Type preset_type = tab->type();
 | 
			
		||||
        if (preset_type == Slic3r::Preset::TYPE_INVALID){
 | 
			
		||||
        if (preset_type == Slic3r::Preset::TYPE_INVALID) {
 | 
			
		||||
            wxASSERT(false);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -737,7 +739,7 @@ void MainFrame::on_value_changed(wxCommandEvent& event)
 | 
			
		|||
    auto opt_key = event.GetString();
 | 
			
		||||
    if (m_plater) {
 | 
			
		||||
        m_plater->on_config_change(*tab->get_config()); // propagate config change events to the plater
 | 
			
		||||
        if (opt_key == "extruders_count"){
 | 
			
		||||
        if (opt_key == "extruders_count") {
 | 
			
		||||
            auto value = event.GetInt();
 | 
			
		||||
            m_plater->on_extruders_change(value);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,13 +66,13 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
 | 
			
		|||
    }
 | 
			
		||||
    // Grab a reference to fields for convenience
 | 
			
		||||
    const t_field& field = m_fields[id];
 | 
			
		||||
	field->m_on_change = [this](std::string opt_id, boost::any value){
 | 
			
		||||
	field->m_on_change = [this](std::string opt_id, boost::any value) {
 | 
			
		||||
			//! This function will be called from Field.					
 | 
			
		||||
			//! Call OptionGroup._on_change(...)
 | 
			
		||||
			if (!m_disabled) 
 | 
			
		||||
				this->on_change_OG(opt_id, value);
 | 
			
		||||
	};
 | 
			
		||||
	field->m_on_kill_focus = [this](){
 | 
			
		||||
	field->m_on_kill_focus = [this]() {
 | 
			
		||||
			//! This function will be called from Field.					
 | 
			
		||||
			if (!m_disabled) 
 | 
			
		||||
				this->on_kill_focus();
 | 
			
		||||
| 
						 | 
				
			
			@ -81,11 +81,11 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
 | 
			
		|||
	
 | 
			
		||||
	//! Label to change background color, when option is modified
 | 
			
		||||
	field->m_Label = label;
 | 
			
		||||
	field->m_back_to_initial_value = [this](std::string opt_id){
 | 
			
		||||
	field->m_back_to_initial_value = [this](std::string opt_id) {
 | 
			
		||||
		if (!m_disabled)
 | 
			
		||||
			this->back_to_initial_value(opt_id);
 | 
			
		||||
	};
 | 
			
		||||
	field->m_back_to_sys_value = [this](std::string opt_id){
 | 
			
		||||
	field->m_back_to_sys_value = [this](std::string opt_id) {
 | 
			
		||||
		if (!this->m_disabled)
 | 
			
		||||
			this->back_to_sys_value(opt_id);
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -107,8 +107,8 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/* = nullptr*/) {
 | 
			
		||||
//!    if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){
 | 
			
		||||
	if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){
 | 
			
		||||
//!    if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)) {
 | 
			
		||||
	if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width) {
 | 
			
		||||
		if (line.sizer != nullptr) {
 | 
			
		||||
            sizer->Add(line.sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
 | 
			
		||||
            return;
 | 
			
		||||
| 
						 | 
				
			
			@ -313,7 +313,7 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b
 | 
			
		|||
		// get value
 | 
			
		||||
//!		auto field_value = get_value(opt_id);
 | 
			
		||||
		if (option.gui_flags.compare("serialized")==0) {
 | 
			
		||||
			if (opt_index != -1){
 | 
			
		||||
			if (opt_index != -1) {
 | 
			
		||||
				// 		die "Can't set serialized option indexed value" ;
 | 
			
		||||
			}
 | 
			
		||||
			change_opt_value(*m_config, opt_key, value);
 | 
			
		||||
| 
						 | 
				
			
			@ -355,7 +355,7 @@ void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key)
 | 
			
		|||
void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key)
 | 
			
		||||
{
 | 
			
		||||
	boost::any value;
 | 
			
		||||
	if (opt_key == "extruders_count"){
 | 
			
		||||
	if (opt_key == "extruders_count") {
 | 
			
		||||
		auto   *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
 | 
			
		||||
		value = int(nozzle_diameter->values.size());
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -376,7 +376,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
 | 
			
		|||
	on_change_OG(opt_key, get_value(opt_key));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigOptionsGroup::reload_config(){
 | 
			
		||||
void ConfigOptionsGroup::reload_config() {
 | 
			
		||||
	for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
 | 
			
		||||
		auto opt_id = it->first;
 | 
			
		||||
		std::string opt_key = m_opt_map.at(opt_id).first;
 | 
			
		||||
| 
						 | 
				
			
			@ -420,7 +420,7 @@ bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) {
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize){
 | 
			
		||||
boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize) {
 | 
			
		||||
 | 
			
		||||
	if (deserialize) {
 | 
			
		||||
		// Want to edit a vector value(currently only multi - strings) in a single edit box.
 | 
			
		||||
| 
						 | 
				
			
			@ -444,7 +444,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 | 
			
		|||
	boost::any ret;
 | 
			
		||||
	wxString text_value = wxString("");
 | 
			
		||||
	const ConfigOptionDef* opt = config.def()->get(opt_key);
 | 
			
		||||
	switch (opt->type){
 | 
			
		||||
	switch (opt->type) {
 | 
			
		||||
	case coFloatOrPercent:{
 | 
			
		||||
		const auto &value = *config.option<ConfigOptionFloatOrPercent>(opt_key);
 | 
			
		||||
		if (value.percent)
 | 
			
		||||
| 
						 | 
				
			
			@ -477,13 +477,13 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 | 
			
		|||
		ret = static_cast<wxString>(config.opt_string(opt_key));
 | 
			
		||||
		break;
 | 
			
		||||
	case coStrings:
 | 
			
		||||
		if (opt_key.compare("compatible_printers") == 0){
 | 
			
		||||
		if (opt_key.compare("compatible_printers") == 0) {
 | 
			
		||||
			ret = config.option<ConfigOptionStrings>(opt_key)->values;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
 | 
			
		||||
			ret = text_value;
 | 
			
		||||
		else if (opt->gui_flags.compare("serialized") == 0){
 | 
			
		||||
		else if (opt->gui_flags.compare("serialized") == 0) {
 | 
			
		||||
			std::vector<std::string> values = config.option<ConfigOptionStrings>(opt_key)->values;
 | 
			
		||||
			if (!values.empty() && values[0].compare("") != 0)
 | 
			
		||||
				for (auto el : values)
 | 
			
		||||
| 
						 | 
				
			
			@ -507,19 +507,19 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 | 
			
		|||
		break;
 | 
			
		||||
	case coEnum:{
 | 
			
		||||
		if (opt_key.compare("external_fill_pattern") == 0 ||
 | 
			
		||||
			opt_key.compare("fill_pattern") == 0 ){
 | 
			
		||||
			opt_key.compare("fill_pattern") == 0 ) {
 | 
			
		||||
			ret = static_cast<int>(config.option<ConfigOptionEnum<InfillPattern>>(opt_key)->value);
 | 
			
		||||
		}
 | 
			
		||||
		else if (opt_key.compare("gcode_flavor") == 0 ){
 | 
			
		||||
		else if (opt_key.compare("gcode_flavor") == 0 ) {
 | 
			
		||||
			ret = static_cast<int>(config.option<ConfigOptionEnum<GCodeFlavor>>(opt_key)->value);
 | 
			
		||||
		}
 | 
			
		||||
		else if (opt_key.compare("support_material_pattern") == 0){
 | 
			
		||||
		else if (opt_key.compare("support_material_pattern") == 0) {
 | 
			
		||||
			ret = static_cast<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value);
 | 
			
		||||
		}
 | 
			
		||||
		else if (opt_key.compare("seam_position") == 0){
 | 
			
		||||
		else if (opt_key.compare("seam_position") == 0) {
 | 
			
		||||
			ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value);
 | 
			
		||||
		}
 | 
			
		||||
		else if (opt_key.compare("host_type") == 0){
 | 
			
		||||
		else if (opt_key.compare("host_type") == 0) {
 | 
			
		||||
			ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -537,13 +537,14 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index){
 | 
			
		||||
Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index)
 | 
			
		||||
{
 | 
			
		||||
	Field* field = get_field(opt_key);
 | 
			
		||||
	if (field != nullptr)
 | 
			
		||||
		return field;
 | 
			
		||||
	std::string opt_id = "";
 | 
			
		||||
	for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
 | 
			
		||||
		if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second){
 | 
			
		||||
		if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second) {
 | 
			
		||||
			opt_id = it->first;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,7 +156,7 @@ public:
 | 
			
		|||
					column_t extra_clmn = nullptr) :
 | 
			
		||||
					m_parent(_parent), title(title), 
 | 
			
		||||
                    m_show_modified_btns(is_tab_opt),
 | 
			
		||||
					staticbox(title!=""), extra_column(extra_clmn){
 | 
			
		||||
					staticbox(title!=""), extra_column(extra_clmn) {
 | 
			
		||||
        if (staticbox) {
 | 
			
		||||
            stb = new wxStaticBox(_parent, wxID_ANY, title);
 | 
			
		||||
            stb->SetFont(wxGetApp().bold_font());
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +177,7 @@ public:
 | 
			
		|||
#endif /* __WXGTK__ */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wxGridSizer*        get_grid_sizer(){ return m_grid_sizer; }
 | 
			
		||||
    wxGridSizer*        get_grid_sizer() { return m_grid_sizer; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	std::map<t_config_option_key, Option>	m_options;
 | 
			
		||||
| 
						 | 
				
			
			@ -208,10 +208,10 @@ protected:
 | 
			
		|||
	const t_field&		build_field(const Option& opt, wxStaticText* label = nullptr);
 | 
			
		||||
	void				add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field);
 | 
			
		||||
 | 
			
		||||
    virtual void		on_kill_focus (){};
 | 
			
		||||
    virtual void		on_kill_focus () {};
 | 
			
		||||
	virtual void		on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
 | 
			
		||||
	virtual void		back_to_initial_value(const std::string& opt_key){}
 | 
			
		||||
	virtual void		back_to_sys_value(const std::string& opt_key){}
 | 
			
		||||
	virtual void		back_to_initial_value(const std::string& opt_key) {}
 | 
			
		||||
	virtual void		back_to_sys_value(const std::string& opt_key) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ConfigOptionsGroup: public OptionsGroup {
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +225,7 @@ public:
 | 
			
		|||
    bool					m_full_labels {0};
 | 
			
		||||
	t_opt_map				m_opt_map;
 | 
			
		||||
 | 
			
		||||
    void        set_config(DynamicPrintConfig* config){ m_config = config; }
 | 
			
		||||
    void        set_config(DynamicPrintConfig* config) { m_config = config; }
 | 
			
		||||
	Option		get_option(const std::string& opt_key, int opt_index = -1);
 | 
			
		||||
	Line		create_single_option_line(const std::string& title, int idx = -1) /*const*/{
 | 
			
		||||
		Option option = get_option(title, idx);
 | 
			
		||||
| 
						 | 
				
			
			@ -258,8 +258,8 @@ public:
 | 
			
		|||
class ogStaticText :public wxStaticText{
 | 
			
		||||
public:
 | 
			
		||||
	ogStaticText() {}
 | 
			
		||||
	ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){}
 | 
			
		||||
	~ogStaticText(){}
 | 
			
		||||
	ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize) {}
 | 
			
		||||
	~ogStaticText() {}
 | 
			
		||||
 | 
			
		||||
	void		SetText(const wxString& value, bool wrap = true);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,18 +132,27 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
 | 
			
		|||
    Add(grid_sizer, 0, wxEXPAND);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum SlisedInfoIdx
 | 
			
		||||
{
 | 
			
		||||
    siFilament_m,
 | 
			
		||||
    siFilament_mm3,
 | 
			
		||||
    siFilament_g,
 | 
			
		||||
    siCost,
 | 
			
		||||
    siTimeNormal,
 | 
			
		||||
    siTimeSilent,
 | 
			
		||||
    siWTNumbetOfToolchanges,
 | 
			
		||||
 | 
			
		||||
    siCount
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class SlicedInfo : public wxStaticBoxSizer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SlicedInfo(wxWindow *parent);
 | 
			
		||||
    void SetTextAndShow(SlisedInfoIdx idx, const wxString& text);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    wxStaticText *info_filament_m;
 | 
			
		||||
    wxStaticText *info_filament_mm3;
 | 
			
		||||
    wxStaticText *info_filament_g;
 | 
			
		||||
    wxStaticText *info_cost;
 | 
			
		||||
    wxStaticText *info_time_normal;
 | 
			
		||||
    wxStaticText *info_time_silent;
 | 
			
		||||
    std::vector<std::pair<wxStaticText*, wxStaticText*>> info_vec;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SlicedInfo::SlicedInfo(wxWindow *parent) :
 | 
			
		||||
| 
						 | 
				
			
			@ -155,23 +164,37 @@ SlicedInfo::SlicedInfo(wxWindow *parent) :
 | 
			
		|||
    grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
 | 
			
		||||
    grid_sizer->AddGrowableCol(1, 1);
 | 
			
		||||
 | 
			
		||||
    auto init_info_label = [parent, grid_sizer](wxStaticText *&info_label, wxString text_label) {
 | 
			
		||||
    info_vec.reserve(siCount);
 | 
			
		||||
 | 
			
		||||
    auto init_info_label = [this, parent, grid_sizer](wxString text_label) {
 | 
			
		||||
        auto *text = new wxStaticText(parent, wxID_ANY, text_label);
 | 
			
		||||
        text->SetFont(wxGetApp().small_font());
 | 
			
		||||
        info_label = new wxStaticText(parent, wxID_ANY, "N/A");
 | 
			
		||||
        auto info_label = new wxStaticText(parent, wxID_ANY, "N/A");
 | 
			
		||||
        info_label->SetFont(wxGetApp().small_font());
 | 
			
		||||
        grid_sizer->Add(text, 0);
 | 
			
		||||
        grid_sizer->Add(info_label, 0);
 | 
			
		||||
        info_vec.push_back(std::pair<wxStaticText*, wxStaticText*>(text, info_label));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    init_info_label(info_filament_m, _(L("Used Filament (m)")));
 | 
			
		||||
    init_info_label(info_filament_mm3, _(L("Used Filament (mm³)")));
 | 
			
		||||
    init_info_label(info_filament_g, _(L("Used Filament (g)")));
 | 
			
		||||
    init_info_label(info_cost, _(L("Cost")));
 | 
			
		||||
    init_info_label(info_time_normal, _(L("Estimated printing time (normal mode)")));
 | 
			
		||||
    init_info_label(info_time_silent, _(L("Estimated printing time (silent mode)")));
 | 
			
		||||
    init_info_label(_(L("Used Filament (m)")));
 | 
			
		||||
    init_info_label(_(L("Used Filament (mm³)")));
 | 
			
		||||
    init_info_label(_(L("Used Filament (g)")));
 | 
			
		||||
    init_info_label(_(L("Cost")));
 | 
			
		||||
    init_info_label(_(L("Estimated printing time (normal mode)")));
 | 
			
		||||
    init_info_label(_(L("Estimated printing time (silent mode)")));
 | 
			
		||||
    init_info_label(_(L("Number of tool changes")));
 | 
			
		||||
 | 
			
		||||
    Add(grid_sizer, 0, wxEXPAND);
 | 
			
		||||
    this->Show(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text)
 | 
			
		||||
{
 | 
			
		||||
    const bool show = text != "N/A";
 | 
			
		||||
    if (show)
 | 
			
		||||
        info_vec[idx].second->SetLabelText(text);
 | 
			
		||||
    info_vec[idx].first->Show(show);
 | 
			
		||||
    info_vec[idx].second->Show(show);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
 | 
			
		||||
| 
						 | 
				
			
			@ -256,13 +279,13 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
 | 
			
		|||
    m_og->set_config(config);
 | 
			
		||||
    m_og->label_width = label_width;
 | 
			
		||||
 | 
			
		||||
    m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
    m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
        TabPrint* tab_print = nullptr;
 | 
			
		||||
        for (size_t i = 0; i < wxGetApp().tab_panel()->GetPageCount(); ++i) {
 | 
			
		||||
            Tab *tab = dynamic_cast<Tab*>(wxGetApp().tab_panel()->GetPage(i));
 | 
			
		||||
            if (!tab)
 | 
			
		||||
                continue;
 | 
			
		||||
            if (tab->name() == "print"){
 | 
			
		||||
            if (tab->name() == "print") {
 | 
			
		||||
                tab_print = static_cast<TabPrint*>(tab);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -270,14 +293,14 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
 | 
			
		|||
        if (tab_print == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (opt_key == "fill_density"){
 | 
			
		||||
        if (opt_key == "fill_density") {
 | 
			
		||||
            value = m_og->get_config_value(*config, opt_key);
 | 
			
		||||
            tab_print->set_value(opt_key, value);
 | 
			
		||||
            tab_print->update();
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            DynamicPrintConfig new_conf = *config;
 | 
			
		||||
            if (opt_key == "brim"){
 | 
			
		||||
            if (opt_key == "brim") {
 | 
			
		||||
                double new_val;
 | 
			
		||||
                double brim_width = config->opt_float("brim_width");
 | 
			
		||||
                if (boost::any_cast<bool>(value) == true)
 | 
			
		||||
| 
						 | 
				
			
			@ -344,7 +367,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
    Line line = { "", "" };
 | 
			
		||||
    line.widget = [config, this](wxWindow* parent){
 | 
			
		||||
    line.widget = [config, this](wxWindow* parent) {
 | 
			
		||||
        m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
 | 
			
		||||
        auto sizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
        sizer->Add(m_wiping_dialog_button);
 | 
			
		||||
| 
						 | 
				
			
			@ -642,7 +665,43 @@ void Sidebar::show_info_sizers(const bool show)
 | 
			
		|||
{
 | 
			
		||||
    p->object_info->Show(show);
 | 
			
		||||
    p->object_info->manifold_warning_icon->Show(show && p->show_manifold_warning_icon); // where is g_show_manifold_warning_icon updating? #ys_FIXME
 | 
			
		||||
    p->sliced_info->Show(show && p->show_print_info); // where is g_show_print_info updating? #ys_FIXME
 | 
			
		||||
//     p->sliced_info->Show(show && p->show_print_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Sidebar::show_sliced_info_sizer(const bool show) 
 | 
			
		||||
{
 | 
			
		||||
    p->plater->Freeze();
 | 
			
		||||
//     p->show_print_info = show;
 | 
			
		||||
    p->sliced_info->Show(show);
 | 
			
		||||
    if (show) {
 | 
			
		||||
        const PrintStatistics& ps = p->plater->print().print_statistics();
 | 
			
		||||
        const bool is_wipe_tower = ps.total_wipe_tower_filament > 0;
 | 
			
		||||
 | 
			
		||||
        wxString info_text = is_wipe_tower ?
 | 
			
		||||
                            wxString::Format("%.2f  (%.2f %s + %.2f %s)", ps.total_used_filament / 1000,
 | 
			
		||||
                                            (ps.total_used_filament - ps.total_wipe_tower_filament) / 1000, _(L("objects")),
 | 
			
		||||
                                            ps.total_wipe_tower_filament / 1000, _(L("wipe tower"))) :
 | 
			
		||||
                            wxString::Format("%.2f", ps.total_used_filament / 1000);
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siFilament_m,    info_text);
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siFilament_mm3,  wxString::Format("%.2f", ps.total_extruded_volume));
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siFilament_g,    wxString::Format("%.2f", ps.total_weight));
 | 
			
		||||
 | 
			
		||||
        info_text = is_wipe_tower ?
 | 
			
		||||
                    wxString::Format("%.2f  (%.2f %s + %.2f %s)", ps.total_cost,
 | 
			
		||||
                                    (ps.total_cost - ps.total_wipe_tower_cost), _(L("objects")),
 | 
			
		||||
                                    ps.total_wipe_tower_cost, _(L("wipe tower"))) :
 | 
			
		||||
                    wxString::Format("%.2f", ps.total_cost);
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siCost,       info_text);
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siTimeNormal, ps.estimated_normal_print_time);
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siTimeSilent, ps.estimated_silent_print_time);
 | 
			
		||||
 | 
			
		||||
        // if there is a wipe tower, insert number of toolchanges info into the array:
 | 
			
		||||
        p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->print().wipe_tower_data().number_of_toolchanges) : "N/A");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p->scrolled->Layout();
 | 
			
		||||
    p->plater->Layout();
 | 
			
		||||
    p->plater->Thaw();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Sidebar::show_buttons(const bool show)
 | 
			
		||||
| 
						 | 
				
			
			@ -902,7 +961,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
 | 
			
		|||
    background_process.set_sliced_event(EVT_SLICING_COMPLETED);
 | 
			
		||||
    background_process.set_finished_event(EVT_PROCESS_COMPLETED);
 | 
			
		||||
    // Register progress callback from the Print class to the Platter.
 | 
			
		||||
    print.set_status_callback([this](int percent, const std::string &message){
 | 
			
		||||
    print.set_status_callback([this](int percent, const std::string &message) {
 | 
			
		||||
        wxCommandEvent event(EVT_PROGRESS_BAR);
 | 
			
		||||
        event.SetInt(percent);
 | 
			
		||||
        event.SetString(message);
 | 
			
		||||
| 
						 | 
				
			
			@ -932,7 +991,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
 | 
			
		|||
    _3DScene::enable_force_zoom_to_bed(canvas3D, true);
 | 
			
		||||
 | 
			
		||||
    this->background_process_timer.SetOwner(this->q, 0);
 | 
			
		||||
    this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt){ this->async_apply_config(); });
 | 
			
		||||
    this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->async_apply_config(); });
 | 
			
		||||
 | 
			
		||||
    auto *bed_shape = config->opt<ConfigOptionPoints>("bed_shape");
 | 
			
		||||
    _3DScene::set_bed_shape(canvas3D, bed_shape->values);
 | 
			
		||||
| 
						 | 
				
			
			@ -1160,6 +1219,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_
 | 
			
		|||
        if (type_3mf) {
 | 
			
		||||
            for (ModelObject* model_object : model.objects) {
 | 
			
		||||
                model_object->center_around_origin();
 | 
			
		||||
                model_object->ensure_on_bed();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1244,6 +1304,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        object->ensure_on_bed();
 | 
			
		||||
 | 
			
		||||
        // print.auto_assign_extruders(object);
 | 
			
		||||
        // print.add_model_object(object);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1627,23 +1689,30 @@ void Plater::priv::split_object()
 | 
			
		|||
    ModelObjectPtrs new_objects;
 | 
			
		||||
    current_model_object->split(&new_objects);
 | 
			
		||||
    if (new_objects.size() == 1)
 | 
			
		||||
    {
 | 
			
		||||
        Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part.")));
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        unsigned int counter = 1;
 | 
			
		||||
        for (ModelObject* m : new_objects)
 | 
			
		||||
        {
 | 
			
		||||
            m->name = current_model_object->name + "_" + std::to_string(counter++);
 | 
			
		||||
            m->center_around_origin();
 | 
			
		||||
            for (ModelInstance* i : current_model_object->instances)
 | 
			
		||||
            {
 | 
			
		||||
                m->add_instance(*i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        remove(obj_idx);
 | 
			
		||||
 | 
			
		||||
        // load all model objects at once, otherwise the plate would be rearranged after each one
 | 
			
		||||
        // causing original positions not to be kept
 | 
			
		||||
        load_model_objects(new_objects);
 | 
			
		||||
        std::vector<size_t> idxs = load_model_objects(new_objects);
 | 
			
		||||
 | 
			
		||||
        // select newly added objects
 | 
			
		||||
        for (size_t idx : idxs)
 | 
			
		||||
        {
 | 
			
		||||
            get_selection().add_object((unsigned int)idx, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif // ENABLE_EXTENDED_SELECTION
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1677,7 +1746,7 @@ void Plater::priv::async_apply_config()
 | 
			
		|||
    if (invalidated == Print::APPLY_STATUS_INVALIDATED) {
 | 
			
		||||
        // Some previously calculated data on the Print was invalidated.
 | 
			
		||||
        // Hide the slicing results, as the current slicing status is no more valid.
 | 
			
		||||
        this->sidebar->show_info_sizers(false);
 | 
			
		||||
        this->sidebar->show_sliced_info_sizer(false);
 | 
			
		||||
        // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
 | 
			
		||||
        // Otherwise they will be just refreshed.
 | 
			
		||||
        this->gcode_preview_data.reset();
 | 
			
		||||
| 
						 | 
				
			
			@ -1694,7 +1763,7 @@ void Plater::priv::async_apply_config()
 | 
			
		|||
    }
 | 
			
		||||
    if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->get_config("background_processing") == "1" &&
 | 
			
		||||
        this->print.num_object_instances() > 0 && this->background_process.start())
 | 
			
		||||
		this->statusbar()->set_cancel_callback([this](){
 | 
			
		||||
		this->statusbar()->set_cancel_callback([this]() {
 | 
			
		||||
            this->statusbar()->set_status_text(L("Cancelling"));
 | 
			
		||||
			this->background_process.stop();
 | 
			
		||||
        });
 | 
			
		||||
| 
						 | 
				
			
			@ -1857,7 +1926,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
 | 
			
		|||
	if (canceled)
 | 
			
		||||
		this->statusbar()->set_status_text(L("Cancelled"));
 | 
			
		||||
 | 
			
		||||
	this->sidebar->show_info_sizers(success);
 | 
			
		||||
    this->sidebar->show_sliced_info_sizer(success);
 | 
			
		||||
 | 
			
		||||
    // this updates buttons status
 | 
			
		||||
    //$self->object_list_changed;
 | 
			
		||||
| 
						 | 
				
			
			@ -2030,13 +2099,13 @@ void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)
 | 
			
		|||
bool Plater::priv::init_object_menu()
 | 
			
		||||
{
 | 
			
		||||
    wxMenuItem* item_delete = append_menu_item(&object_menu, wxID_ANY, _(L("Delete\tDel")), _(L("Remove the selected object")),
 | 
			
		||||
        [this](wxCommandEvent&){ q->remove_selected(); }, "brick_delete.png");
 | 
			
		||||
        [this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
 | 
			
		||||
    wxMenuItem* item_increase = append_menu_item(&object_menu, wxID_ANY, _(L("Increase copies\t+")), _(L("Place one more copy of the selected object")),
 | 
			
		||||
        [this](wxCommandEvent&){ q->increase_instances(); }, "add.png");
 | 
			
		||||
        [this](wxCommandEvent&) { q->increase_instances(); }, "add.png");
 | 
			
		||||
    wxMenuItem* item_decrease = append_menu_item(&object_menu, wxID_ANY, _(L("Decrease copies\t-")), _(L("Remove one copy of the selected object")),
 | 
			
		||||
        [this](wxCommandEvent&){ q->decrease_instances(); }, "delete.png");
 | 
			
		||||
        [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png");
 | 
			
		||||
    wxMenuItem* item_set_number_of_copies = append_menu_item(&object_menu, wxID_ANY, _(L("Set number of copies…")), _(L("Change the number of copies of the selected object")),
 | 
			
		||||
        [this](wxCommandEvent&){ q->set_number_of_copies(); }, "textfield.png");
 | 
			
		||||
        [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png");
 | 
			
		||||
 | 
			
		||||
    object_menu.AppendSeparator();
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -2046,11 +2115,11 @@ bool Plater::priv::init_object_menu()
 | 
			
		|||
        return false;
 | 
			
		||||
 | 
			
		||||
    append_menu_item(mirror_menu, wxID_ANY, _(L("Along X axis")), _(L("Mirror the selected object along the X axis")),
 | 
			
		||||
        [this](wxCommandEvent&){ mirror(X); }, "bullet_red.png", &object_menu);
 | 
			
		||||
        [this](wxCommandEvent&) { mirror(X); }, "bullet_red.png", &object_menu);
 | 
			
		||||
    append_menu_item(mirror_menu, wxID_ANY, _(L("Along Y axis")), _(L("Mirror the selected object along the Y axis")),
 | 
			
		||||
        [this](wxCommandEvent&){ mirror(Y); }, "bullet_green.png", &object_menu);
 | 
			
		||||
        [this](wxCommandEvent&) { mirror(Y); }, "bullet_green.png", &object_menu);
 | 
			
		||||
    append_menu_item(mirror_menu, wxID_ANY, _(L("Along Z axis")), _(L("Mirror the selected object along the Z axis")),
 | 
			
		||||
        [this](wxCommandEvent&){ mirror(Z); }, "bullet_blue.png", &object_menu);
 | 
			
		||||
        [this](wxCommandEvent&) { mirror(Z); }, "bullet_blue.png", &object_menu);
 | 
			
		||||
 | 
			
		||||
    wxMenuItem* item_mirror = append_submenu(&object_menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object")));
 | 
			
		||||
#endif // ENABLE_MIRROR
 | 
			
		||||
| 
						 | 
				
			
			@ -2060,9 +2129,9 @@ bool Plater::priv::init_object_menu()
 | 
			
		|||
        return false;
 | 
			
		||||
 | 
			
		||||
    wxMenuItem* item_split_objects = append_menu_item(split_menu, wxID_ANY, _(L("To objects")), _(L("Split the selected object into individual objects")),
 | 
			
		||||
        [this](wxCommandEvent&){ split_object(); }, "shape_ungroup.png", &object_menu);
 | 
			
		||||
        [this](wxCommandEvent&) { split_object(); }, "shape_ungroup.png", &object_menu);
 | 
			
		||||
    wxMenuItem* item_split_volumes = append_menu_item(split_menu, wxID_ANY, _(L("To parts")), _(L("Split the selected object into individual sub-parts")),
 | 
			
		||||
        [this](wxCommandEvent&){ split_volume(); }, "shape_ungroup.png", &object_menu);
 | 
			
		||||
        [this](wxCommandEvent&) { split_volume(); }, "shape_ungroup.png", &object_menu);
 | 
			
		||||
 | 
			
		||||
    wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2455,7 +2524,7 @@ void Plater::reslice()
 | 
			
		|||
//    this->p->stop_background_process();
 | 
			
		||||
    // Rather perform one additional unnecessary update of the print object instead of skipping a pending async update.
 | 
			
		||||
    this->p->async_apply_config();
 | 
			
		||||
	this->p->statusbar()->set_cancel_callback([this](){
 | 
			
		||||
	this->p->statusbar()->set_cancel_callback([this]() {
 | 
			
		||||
		this->p->statusbar()->set_status_text(L("Cancelling"));
 | 
			
		||||
		this->p->background_process.stop();
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -2548,7 +2617,7 @@ wxGLCanvas* Plater::canvas3D()
 | 
			
		|||
    return p->canvas3D;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Plater::changed_object_settings(int obj_idx)
 | 
			
		||||
void Plater::changed_object(int obj_idx)
 | 
			
		||||
{
 | 
			
		||||
    if (obj_idx < 0)
 | 
			
		||||
        return;
 | 
			
		||||
| 
						 | 
				
			
			@ -2561,6 +2630,8 @@ void Plater::changed_object_settings(int obj_idx)
 | 
			
		|||
        // recenter and re - align to Z = 0
 | 
			
		||||
        auto model_object = p->model.objects[obj_idx];
 | 
			
		||||
        model_object->center_around_origin();
 | 
			
		||||
        model_object->ensure_on_bed();
 | 
			
		||||
        _3DScene::reload_scene(p->canvas3D, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // update print
 | 
			
		||||
| 
						 | 
				
			
			@ -2571,7 +2642,6 @@ void Plater::changed_object_settings(int obj_idx)
 | 
			
		|||
        auto selections = p->collect_selections();
 | 
			
		||||
        _3DScene::set_objects_selections(p->canvas3D, selections);
 | 
			
		||||
#endif // !ENABLE_EXTENDED_SELECTION
 | 
			
		||||
        _3DScene::reload_scene(p->canvas3D, false);
 | 
			
		||||
#if !ENABLE_MODIFIED_CAMERA_TARGET
 | 
			
		||||
        _3DScene::zoom_to_volumes(p->canvas3D);
 | 
			
		||||
#endif // !ENABLE_MODIFIED_CAMERA_TARGET
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,6 +78,7 @@ public:
 | 
			
		|||
    wxButton*               get_wiping_dialog_button();
 | 
			
		||||
    void                    update_objects_list_extruder_column(int extruders_count);
 | 
			
		||||
    void                    show_info_sizers(const bool show);
 | 
			
		||||
    void                    show_sliced_info_sizer(const bool show);
 | 
			
		||||
    void                    show_buttons(const bool show);
 | 
			
		||||
    void                    show_button(ButtonAction but_action, bool show);
 | 
			
		||||
    void                    enable_buttons(bool enable);
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +123,7 @@ public:
 | 
			
		|||
    void export_amf();
 | 
			
		||||
    void export_3mf();
 | 
			
		||||
    void reslice();
 | 
			
		||||
    void changed_object_settings(int obj_idx);
 | 
			
		||||
    void changed_object(int obj_idx);
 | 
			
		||||
    void send_gcode();
 | 
			
		||||
 | 
			
		||||
    void on_extruders_change(int extruders_count);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ class PreferencesDialog : public wxDialog
 | 
			
		|||
	std::shared_ptr<ConfigOptionsGroup>	m_optgroup;
 | 
			
		||||
public:
 | 
			
		||||
	PreferencesDialog(wxWindow* parent);
 | 
			
		||||
	~PreferencesDialog(){ }
 | 
			
		||||
	~PreferencesDialog() {}
 | 
			
		||||
 | 
			
		||||
	void	build();
 | 
			
		||||
	void	accept();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -795,7 +795,7 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
 | 
			
		|||
			bmp = m_bitmap_cache->insert(bitmap_key, bmps);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (preset.is_default || preset.is_system){
 | 
			
		||||
		if (preset.is_default || preset.is_system) {
 | 
			
		||||
			ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
 | 
			
		||||
				(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
 | 
			
		||||
			if (i == m_idx_selected)
 | 
			
		||||
| 
						 | 
				
			
			@ -855,7 +855,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
 | 
			
		|||
			bmp = m_bitmap_cache->insert(bitmap_key, bmps);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (preset.is_default || preset.is_system){
 | 
			
		||||
		if (preset.is_default || preset.is_system) {
 | 
			
		||||
			ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
 | 
			
		||||
				(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
 | 
			
		||||
			if (i == m_idx_selected)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1183,7 +1183,7 @@ void PresetBundle::update_compatible_with_printer(bool select_other_if_incompati
 | 
			
		|||
        prefered_print_profile.empty() ?
 | 
			
		||||
            this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
 | 
			
		||||
            this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
 | 
			
		||||
                [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
 | 
			
		||||
                [&prefered_print_profile](const std::string& profile_name) { return profile_name == prefered_print_profile; });
 | 
			
		||||
        prefered_filament_profiles.empty() ?
 | 
			
		||||
            this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
 | 
			
		||||
            this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
 | 
			
		||||
| 
						 | 
				
			
			@ -1203,7 +1203,7 @@ void PresetBundle::update_compatible_with_printer(bool select_other_if_incompati
 | 
			
		|||
                        const std::string &preferred = (idx < prefered_filament_profiles.size()) ? 
 | 
			
		||||
                            prefered_filament_profiles[idx] : prefered_filament_profiles.front();
 | 
			
		||||
                        filament_name = this->filaments.first_compatible(
 | 
			
		||||
                            [&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
 | 
			
		||||
                            [&preferred](const std::string& profile_name) { return profile_name == preferred; }).name;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -1215,7 +1215,7 @@ void PresetBundle::update_compatible_with_printer(bool select_other_if_incompati
 | 
			
		|||
        prefered_sla_material_profile.empty() ?
 | 
			
		||||
            this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
 | 
			
		||||
			this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
 | 
			
		||||
                [&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; });        
 | 
			
		||||
                [&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; });
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1364,7 +1364,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
 | 
			
		|||
            bitmap = m_bitmapCache->insert(bitmap_key, bmps);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (preset.is_default || preset.is_system){
 | 
			
		||||
		if (preset.is_default || preset.is_system) {
 | 
			
		||||
			ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), 
 | 
			
		||||
				(bitmap == 0) ? wxNullBitmap : *bitmap);
 | 
			
		||||
			if (selected)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,16 +125,16 @@ void Tab::create_preset_tab()
 | 
			
		|||
	set_tooltips_text();
 | 
			
		||||
 | 
			
		||||
	m_undo_btn->SetBitmap(m_bmp_white_bullet);
 | 
			
		||||
	m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(); }));
 | 
			
		||||
	m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_roll_back_value(); }));
 | 
			
		||||
	m_undo_to_sys_btn->SetBitmap(m_bmp_white_bullet);
 | 
			
		||||
	m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(true); }));
 | 
			
		||||
	m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_roll_back_value(true); }));
 | 
			
		||||
	m_question_btn->SetBitmap(m_bmp_question);
 | 
			
		||||
	m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent)
 | 
			
		||||
	{
 | 
			
		||||
		auto dlg = new ButtonsDescription(this, &m_icon_descriptions);
 | 
			
		||||
		if (dlg->ShowModal() == wxID_OK){
 | 
			
		||||
		if (dlg->ShowModal() == wxID_OK) {
 | 
			
		||||
			// Colors for ui "decoration"
 | 
			
		||||
            for (Tab *tab : wxGetApp().tabs_list){
 | 
			
		||||
            for (Tab *tab : wxGetApp().tabs_list) {
 | 
			
		||||
                tab->m_sys_label_clr = wxGetApp().get_label_clr_sys();
 | 
			
		||||
                tab->m_modified_label_clr = wxGetApp().get_label_clr_modified();
 | 
			
		||||
				tab->update_labels_colour();
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +185,7 @@ void Tab::create_preset_tab()
 | 
			
		|||
	m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this);
 | 
			
		||||
	m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this);
 | 
			
		||||
 | 
			
		||||
	m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e){
 | 
			
		||||
	m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) {
 | 
			
		||||
		//! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, 
 | 
			
		||||
		//! but the OSX version derived from wxOwnerDrawnCombo, instead of:
 | 
			
		||||
		//! select_preset(m_presets_choice->GetStringSelection().ToStdString()); 
 | 
			
		||||
| 
						 | 
				
			
			@ -193,11 +193,11 @@ void Tab::create_preset_tab()
 | 
			
		|||
		int selected_item = m_presets_choice->GetSelection();
 | 
			
		||||
		if (m_selected_preset_item == selected_item && !m_presets->current_is_dirty())
 | 
			
		||||
			return;
 | 
			
		||||
		if (selected_item >= 0){
 | 
			
		||||
		if (selected_item >= 0) {
 | 
			
		||||
			std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data();
 | 
			
		||||
			if (selected_string.find("-------") == 0
 | 
			
		||||
				/*selected_string == "------- System presets -------" ||
 | 
			
		||||
				selected_string == "-------  User presets  -------"*/){
 | 
			
		||||
				selected_string == "-------  User presets  -------"*/) {
 | 
			
		||||
				m_presets_choice->SetSelection(m_selected_preset_item);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -206,9 +206,9 @@ void Tab::create_preset_tab()
 | 
			
		|||
		}
 | 
			
		||||
	}));
 | 
			
		||||
 | 
			
		||||
	m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ save_preset(); }));
 | 
			
		||||
	m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ delete_preset(); }));
 | 
			
		||||
	m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){
 | 
			
		||||
	m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); }));
 | 
			
		||||
	m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); }));
 | 
			
		||||
	m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) {
 | 
			
		||||
		toggle_show_hide_incompatible();
 | 
			
		||||
	}));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -281,7 +281,7 @@ void Tab::update_labels_colour()
 | 
			
		|||
		const wxColour *color = &m_sys_label_clr;
 | 
			
		||||
 | 
			
		||||
		// value isn't equal to system value
 | 
			
		||||
		if ((opt.second & osSystemValue) == 0){
 | 
			
		||||
		if ((opt.second & osSystemValue) == 0) {
 | 
			
		||||
			// value is equal to last saved
 | 
			
		||||
			if ((opt.second & osInitValue) != 0)
 | 
			
		||||
				color = &m_default_text_clr;
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +304,7 @@ void Tab::update_labels_colour()
 | 
			
		|||
	Thaw();
 | 
			
		||||
 | 
			
		||||
	auto cur_item = m_treectrl->GetFirstVisibleItem();
 | 
			
		||||
	while (cur_item){
 | 
			
		||||
	while (cur_item) {
 | 
			
		||||
		auto title = m_treectrl->GetItemText(cur_item);
 | 
			
		||||
		for (auto page : m_pages)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -331,7 +331,7 @@ void Tab::update_changed_ui()
 | 
			
		|||
	const bool deep_compare = (m_name == "printer" || m_name == "sla_material");
 | 
			
		||||
	auto dirty_options = m_presets->current_dirty_options(deep_compare);
 | 
			
		||||
	auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare);
 | 
			
		||||
    if (name() == "printer"){
 | 
			
		||||
    if (name() == "printer") {
 | 
			
		||||
		TabPrinter* tab = static_cast<TabPrinter*>(this);
 | 
			
		||||
		if (tab->m_initial_extruders_count != tab->m_extruders_count)
 | 
			
		||||
			dirty_options.emplace_back("extruders_count");
 | 
			
		||||
| 
						 | 
				
			
			@ -360,7 +360,7 @@ void Tab::update_changed_ui()
 | 
			
		|||
		const wxString *tt =		&m_tt_value_revert;
 | 
			
		||||
 | 
			
		||||
		// value isn't equal to system value
 | 
			
		||||
		if ((opt.second & osSystemValue) == 0){
 | 
			
		||||
		if ((opt.second & osSystemValue) == 0) {
 | 
			
		||||
			is_nonsys_value = true;
 | 
			
		||||
			sys_icon = m_bmp_non_system;
 | 
			
		||||
			sys_tt = m_tt_non_system;
 | 
			
		||||
| 
						 | 
				
			
			@ -426,7 +426,7 @@ void TabPrinter::init_options_list()
 | 
			
		|||
 | 
			
		||||
	for (const auto opt_key : m_config->keys())
 | 
			
		||||
	{
 | 
			
		||||
		if (opt_key == "bed_shape"){
 | 
			
		||||
		if (opt_key == "bed_shape") {
 | 
			
		||||
			m_options_list.emplace(opt_key, m_opt_status_value);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -451,7 +451,7 @@ void TabSLAMaterial::init_options_list()
 | 
			
		|||
 | 
			
		||||
    for (const auto opt_key : m_config->keys())
 | 
			
		||||
    {
 | 
			
		||||
        if (opt_key == "compatible_printers"){
 | 
			
		||||
        if (opt_key == "compatible_printers") {
 | 
			
		||||
            m_options_list.emplace(opt_key, m_opt_status_value);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -481,7 +481,7 @@ void Tab::update_changed_tree_ui()
 | 
			
		|||
    if (!cur_item || !m_treectrl->IsVisible(cur_item))
 | 
			
		||||
        return;
 | 
			
		||||
	auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
 | 
			
		||||
	while (cur_item){
 | 
			
		||||
	while (cur_item) {
 | 
			
		||||
		auto title = m_treectrl->GetItemText(cur_item);
 | 
			
		||||
		for (auto page : m_pages)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -489,13 +489,13 @@ void Tab::update_changed_tree_ui()
 | 
			
		|||
				continue;
 | 
			
		||||
			bool sys_page = true;
 | 
			
		||||
			bool modified_page = false;
 | 
			
		||||
			if (title == _("General")){
 | 
			
		||||
			if (title == _("General")) {
 | 
			
		||||
				std::initializer_list<const char*> optional_keys{ "extruders_count", "bed_shape" };
 | 
			
		||||
				for (auto &opt_key : optional_keys) {
 | 
			
		||||
					get_sys_and_mod_flags(opt_key, sys_page, modified_page);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (title == _("Dependencies")){
 | 
			
		||||
			if (title == _("Dependencies")) {
 | 
			
		||||
				if (name() != "printer")
 | 
			
		||||
					get_sys_and_mod_flags("compatible_printers", sys_page, modified_page);
 | 
			
		||||
				else {
 | 
			
		||||
| 
						 | 
				
			
			@ -523,7 +523,7 @@ void Tab::update_changed_tree_ui()
 | 
			
		|||
			page->m_is_nonsys_values = !sys_page;
 | 
			
		||||
			page->m_is_modified_values = modified_page;
 | 
			
		||||
 | 
			
		||||
			if (selection == title){
 | 
			
		||||
			if (selection == title) {
 | 
			
		||||
				m_is_nonsys_values = page->m_is_nonsys_values;
 | 
			
		||||
				m_is_modified_values = page->m_is_modified_values;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -561,20 +561,20 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
 | 
			
		|||
	auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
 | 
			
		||||
	for (auto page : m_pages)
 | 
			
		||||
		if (page->title() == selection)	{
 | 
			
		||||
			for (auto group : page->m_optgroups){
 | 
			
		||||
				if (group->title == _("Capabilities")){
 | 
			
		||||
			for (auto group : page->m_optgroups) {
 | 
			
		||||
				if (group->title == _("Capabilities")) {
 | 
			
		||||
					if ((m_options_list["extruders_count"] & os) == 0)
 | 
			
		||||
						to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count");
 | 
			
		||||
				}
 | 
			
		||||
				if (group->title == _("Size and coordinates")){
 | 
			
		||||
					if ((m_options_list["bed_shape"] & os) == 0){
 | 
			
		||||
				if (group->title == _("Size and coordinates")) {
 | 
			
		||||
					if ((m_options_list["bed_shape"] & os) == 0) {
 | 
			
		||||
						to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape");
 | 
			
		||||
						load_key_value("bed_shape", true/*some value*/, true);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
				if (group->title == _("Profile dependencies") && name() != "printer"){
 | 
			
		||||
					if ((m_options_list["compatible_printers"] & os) == 0){
 | 
			
		||||
				if (group->title == _("Profile dependencies") && name() != "printer") {
 | 
			
		||||
					if ((m_options_list["compatible_printers"] & os) == 0) {
 | 
			
		||||
						to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers");
 | 
			
		||||
						load_key_value("compatible_printers", true/*some value*/, true);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -598,7 +598,8 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
 | 
			
		|||
 | 
			
		||||
// Update the combo box label of the selected preset based on its "dirty" state,
 | 
			
		||||
// comparing the selected preset config with $self->{config}.
 | 
			
		||||
void Tab::update_dirty(){
 | 
			
		||||
void Tab::update_dirty()
 | 
			
		||||
{
 | 
			
		||||
	m_presets->update_dirty_ui(m_presets_choice);
 | 
			
		||||
	on_presets_changed();	
 | 
			
		||||
	update_changed_ui();
 | 
			
		||||
| 
						 | 
				
			
			@ -657,7 +658,7 @@ void Tab::update_visibility(ConfigOptionMode mode)
 | 
			
		|||
Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const
 | 
			
		||||
{
 | 
			
		||||
	Field* field = nullptr;
 | 
			
		||||
	for (auto page : m_pages){
 | 
			
		||||
	for (auto page : m_pages) {
 | 
			
		||||
		field = page->get_field(opt_key, opt_index);
 | 
			
		||||
		if (field != nullptr)
 | 
			
		||||
			return field;
 | 
			
		||||
| 
						 | 
				
			
			@ -668,7 +669,7 @@ Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/
 | 
			
		|||
// Set a key/value pair on this page. Return true if the value has been modified.
 | 
			
		||||
// Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer
 | 
			
		||||
// after a preset is loaded.
 | 
			
		||||
bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value){
 | 
			
		||||
bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value) {
 | 
			
		||||
	bool changed = false;
 | 
			
		||||
	for(auto page: m_pages) {
 | 
			
		||||
		if (page->set_value(opt_key, value))
 | 
			
		||||
| 
						 | 
				
			
			@ -786,7 +787,7 @@ void Tab::update_preset_description_line()
 | 
			
		|||
		description_line += "\n\n" + _(L("Additional information:")) + "\n";
 | 
			
		||||
		description_line += "\t" + _(L("vendor")) + ": " + (name()=="printer" ? "\n\t\t" : "") + parent->vendor->name +
 | 
			
		||||
							", ver: " + parent->vendor->config_version.to_string();
 | 
			
		||||
		if (name() == "printer"){
 | 
			
		||||
		if (name() == "printer") {
 | 
			
		||||
			const std::string              &printer_model = preset.config.opt_string("printer_model");
 | 
			
		||||
			//FIXME add prefered_sla_material_profile for SLA
 | 
			
		||||
			const std::string              &default_print_profile = preset.config.opt_string("default_print_profile");
 | 
			
		||||
| 
						 | 
				
			
			@ -798,7 +799,7 @@ void Tab::update_preset_description_line()
 | 
			
		|||
			if (!default_filament_profiles.empty())
 | 
			
		||||
			{
 | 
			
		||||
				description_line += "\n\n\t" + _(L("default filament profile")) + ": \n\t\t";
 | 
			
		||||
				for (auto& profile : default_filament_profiles){
 | 
			
		||||
				for (auto& profile : default_filament_profiles) {
 | 
			
		||||
					if (&profile != &*default_filament_profiles.begin())
 | 
			
		||||
						description_line += ", ";
 | 
			
		||||
					description_line += profile;
 | 
			
		||||
| 
						 | 
				
			
			@ -1043,7 +1044,7 @@ void TabPrint::build()
 | 
			
		|||
	page = add_options_page(_(L("Dependencies")), "wrench.png");
 | 
			
		||||
		optgroup = page->new_optgroup(_(L("Profile dependencies")));
 | 
			
		||||
        line = optgroup->create_single_option_line("compatible_printers");//{ _(L("Compatible printers")), "" };
 | 
			
		||||
		line.widget = [this](wxWindow* parent){
 | 
			
		||||
		line.widget = [this](wxWindow* parent) {
 | 
			
		||||
			return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
 | 
			
		||||
		};
 | 
			
		||||
		optgroup->append_line(line, &m_colored_Label);
 | 
			
		||||
| 
						 | 
				
			
			@ -1061,7 +1062,8 @@ void TabPrint::build()
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
 | 
			
		||||
void TabPrint::reload_config(){
 | 
			
		||||
void TabPrint::reload_config()
 | 
			
		||||
{
 | 
			
		||||
	reload_compatible_printers_widget();
 | 
			
		||||
	Tab::reload_config();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1176,7 +1178,7 @@ void TabPrint::update()
 | 
			
		|||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (!str_fill_pattern.empty()){
 | 
			
		||||
		if (!str_fill_pattern.empty()) {
 | 
			
		||||
			auto external_fill_pattern = m_config->def()->get("external_fill_pattern")->enum_values;
 | 
			
		||||
			bool correct_100p_fill = false;
 | 
			
		||||
			for (auto fill : external_fill_pattern)
 | 
			
		||||
| 
						 | 
				
			
			@ -1186,7 +1188,7 @@ void TabPrint::update()
 | 
			
		|||
			}
 | 
			
		||||
			// get fill_pattern name from enum_labels for using this one at dialog_msg
 | 
			
		||||
			str_fill_pattern = m_config->def()->get("fill_pattern")->enum_labels[fill_pattern];
 | 
			
		||||
			if (!correct_100p_fill){
 | 
			
		||||
			if (!correct_100p_fill) {
 | 
			
		||||
				wxString msg_text = _(L("The ")) + str_fill_pattern + _(L(" infill pattern is not supposed to work at 100% density.\n"
 | 
			
		||||
					"\nShall I switch to rectilinear fill pattern?"));
 | 
			
		||||
				auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO);
 | 
			
		||||
| 
						 | 
				
			
			@ -1369,7 +1371,7 @@ void TabFilament::build()
 | 
			
		|||
        optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
 | 
			
		||||
 | 
			
		||||
        line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" };
 | 
			
		||||
        line.widget = [this](wxWindow* parent){
 | 
			
		||||
        line.widget = [this](wxWindow* parent) {
 | 
			
		||||
			auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
 | 
			
		||||
            auto sizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
			sizer->Add(ramming_dialog_btn);
 | 
			
		||||
| 
						 | 
				
			
			@ -1409,7 +1411,7 @@ void TabFilament::build()
 | 
			
		|||
	page = add_options_page(_(L("Dependencies")), "wrench.png");
 | 
			
		||||
		optgroup = page->new_optgroup(_(L("Profile dependencies")));
 | 
			
		||||
        line = optgroup->create_single_option_line("compatible_printers");//{ _(L("Compatible printers")), "" };
 | 
			
		||||
		line.widget = [this](wxWindow* parent){
 | 
			
		||||
		line.widget = [this](wxWindow* parent) {
 | 
			
		||||
			return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
 | 
			
		||||
		};
 | 
			
		||||
		optgroup->append_line(line, &m_colored_Label);
 | 
			
		||||
| 
						 | 
				
			
			@ -1427,7 +1429,8 @@ void TabFilament::build()
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
 | 
			
		||||
void TabFilament::reload_config(){
 | 
			
		||||
void TabFilament::reload_config()
 | 
			
		||||
{
 | 
			
		||||
	reload_compatible_printers_widget();
 | 
			
		||||
	Tab::reload_config();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1504,7 +1507,7 @@ void TabPrinter::build_fff()
 | 
			
		|||
		auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
 | 
			
		||||
 | 
			
		||||
        Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" };
 | 
			
		||||
		line.widget = [this](wxWindow* parent){
 | 
			
		||||
		line.widget = [this](wxWindow* parent) {
 | 
			
		||||
			auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
 | 
			
		||||
            btn->SetFont(wxGetApp().small_font());
 | 
			
		||||
			btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG));
 | 
			
		||||
| 
						 | 
				
			
			@ -1516,7 +1519,7 @@ void TabPrinter::build_fff()
 | 
			
		|||
			{
 | 
			
		||||
				auto dlg = new BedShapeDialog(this);
 | 
			
		||||
				dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
 | 
			
		||||
				if (dlg->ShowModal() == wxID_OK){
 | 
			
		||||
				if (dlg->ShowModal() == wxID_OK) {
 | 
			
		||||
					load_key_value("bed_shape", dlg->GetValue());
 | 
			
		||||
					update_changed_ui();
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -1540,9 +1543,9 @@ void TabPrinter::build_fff()
 | 
			
		|||
		optgroup->append_single_option_line(option);
 | 
			
		||||
		optgroup->append_single_option_line("single_extruder_multi_material");
 | 
			
		||||
 | 
			
		||||
		optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
		optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
			size_t extruders_count = boost::any_cast<int>(optgroup->get_value("extruders_count"));
 | 
			
		||||
			wxTheApp->CallAfter([this, opt_key, value, extruders_count](){
 | 
			
		||||
			wxTheApp->CallAfter([this, opt_key, value, extruders_count]() {
 | 
			
		||||
				if (opt_key.compare("extruders_count")==0 || opt_key.compare("single_extruder_multi_material")==0) {
 | 
			
		||||
					extruders_count_changed(extruders_count);
 | 
			
		||||
					update_dirty();
 | 
			
		||||
| 
						 | 
				
			
			@ -1563,7 +1566,7 @@ void TabPrinter::build_fff()
 | 
			
		|||
		optgroup = page->new_optgroup(_(L("USB/Serial connection")));
 | 
			
		||||
			line = {_(L("Serial port")), ""};
 | 
			
		||||
			Option serial_port = optgroup->get_option("serial_port");
 | 
			
		||||
			serial_port.side_widget = ([this](wxWindow* parent){
 | 
			
		||||
			serial_port.side_widget = ([this](wxWindow* parent) {
 | 
			
		||||
				auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(Slic3r::var("arrow_rotate_clockwise.png")), wxBITMAP_TYPE_PNG),
 | 
			
		||||
					wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
 | 
			
		||||
				btn->SetToolTip(_(L("Rescan serial ports")));
 | 
			
		||||
| 
						 | 
				
			
			@ -1573,7 +1576,7 @@ void TabPrinter::build_fff()
 | 
			
		|||
				btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {update_serial_ports(); });
 | 
			
		||||
				return sizer;
 | 
			
		||||
			});
 | 
			
		||||
			auto serial_test = [this](wxWindow* parent){
 | 
			
		||||
			auto serial_test = [this](wxWindow* parent) {
 | 
			
		||||
				auto btn = m_serial_test_btn = new wxButton(parent, wxID_ANY,
 | 
			
		||||
					_(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
 | 
			
		||||
				btn->SetFont(Slic3r::GUI::small_font());
 | 
			
		||||
| 
						 | 
				
			
			@ -1581,7 +1584,7 @@ void TabPrinter::build_fff()
 | 
			
		|||
				auto sizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
				sizer->Add(btn);
 | 
			
		||||
 | 
			
		||||
				btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e){
 | 
			
		||||
				btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) {
 | 
			
		||||
					auto sender = Slic3r::make_unique<GCodeSender>();
 | 
			
		||||
					auto res = sender->connect(
 | 
			
		||||
						m_config->opt_string("serial_port"), 
 | 
			
		||||
| 
						 | 
				
			
			@ -1666,7 +1669,7 @@ void TabPrinter::build_fff()
 | 
			
		|||
				auto sizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
				sizer->Add(btn);
 | 
			
		||||
 | 
			
		||||
				btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e){
 | 
			
		||||
				btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) {
 | 
			
		||||
					static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
 | 
			
		||||
					wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
 | 
			
		||||
					if (openFileDialog.ShowModal() != wxID_CANCEL) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1700,8 +1703,8 @@ void TabPrinter::build_fff()
 | 
			
		|||
		optgroup->append_single_option_line("silent_mode");
 | 
			
		||||
		optgroup->append_single_option_line("remaining_times");
 | 
			
		||||
 | 
			
		||||
		optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
			wxTheApp->CallAfter([this, opt_key, value](){
 | 
			
		||||
		optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
			wxTheApp->CallAfter([this, opt_key, value]() {
 | 
			
		||||
				if (opt_key.compare("silent_mode") == 0) {
 | 
			
		||||
					bool val = boost::any_cast<bool>(value);
 | 
			
		||||
					if (m_use_silent_mode != val) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1790,7 +1793,7 @@ void TabPrinter::build_sla()
 | 
			
		|||
    auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
 | 
			
		||||
 | 
			
		||||
    Line line = optgroup->create_single_option_line("bed_shape");//{ _(L("Bed shape")), "" };
 | 
			
		||||
    line.widget = [this](wxWindow* parent){
 | 
			
		||||
    line.widget = [this](wxWindow* parent) {
 | 
			
		||||
        auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
 | 
			
		||||
        //			btn->SetFont(Slic3r::GUI::small_font);
 | 
			
		||||
        btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG));
 | 
			
		||||
| 
						 | 
				
			
			@ -1802,7 +1805,7 @@ void TabPrinter::build_sla()
 | 
			
		|||
        {
 | 
			
		||||
            auto dlg = new BedShapeDialog(this);
 | 
			
		||||
            dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
 | 
			
		||||
            if (dlg->ShowModal() == wxID_OK){
 | 
			
		||||
            if (dlg->ShowModal() == wxID_OK) {
 | 
			
		||||
                load_key_value("bed_shape", dlg->GetValue());
 | 
			
		||||
                update_changed_ui();
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -1852,13 +1855,15 @@ void TabPrinter::build_sla()
 | 
			
		|||
    optgroup->append_line(line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabPrinter::update_serial_ports(){
 | 
			
		||||
void TabPrinter::update_serial_ports()
 | 
			
		||||
{
 | 
			
		||||
	Field *field = get_field("serial_port");
 | 
			
		||||
	Choice *choice = static_cast<Choice *>(field);
 | 
			
		||||
	choice->set_values(Utils::scan_serial_ports());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabPrinter::extruders_count_changed(size_t extruders_count){
 | 
			
		||||
void TabPrinter::extruders_count_changed(size_t extruders_count)
 | 
			
		||||
{
 | 
			
		||||
	m_extruders_count = extruders_count;
 | 
			
		||||
	m_preset_bundle->printers.get_edited_preset().set_num_extruders(extruders_count);
 | 
			
		||||
	m_preset_bundle->update_multi_material_filament_presets();
 | 
			
		||||
| 
						 | 
				
			
			@ -1949,7 +1954,7 @@ void TabPrinter::build_extruder_pages()
 | 
			
		|||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if (existed_page < n_before_extruders && is_marlin_flavor){
 | 
			
		||||
	if (existed_page < n_before_extruders && is_marlin_flavor) {
 | 
			
		||||
		auto page = build_kinematics_page();
 | 
			
		||||
		m_pages.insert(m_pages.begin() + n_before_extruders, page);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1982,7 +1987,7 @@ void TabPrinter::build_extruder_pages()
 | 
			
		|||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){
 | 
			
		||||
	for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) {
 | 
			
		||||
		//# build page
 | 
			
		||||
		char buf[MIN_BUF_LENGTH_FOR_L];
 | 
			
		||||
		sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -2178,7 +2183,8 @@ void TabPrinter::update_fff()
 | 
			
		|||
	Thaw();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabPrinter::update_sla(){ ; }
 | 
			
		||||
void TabPrinter::update_sla()
 | 
			
		||||
{ ; }
 | 
			
		||||
 | 
			
		||||
// Initialize the UI from the current preset
 | 
			
		||||
void Tab::load_current_preset()
 | 
			
		||||
| 
						 | 
				
			
			@ -2501,7 +2507,7 @@ void Tab::save_preset(std::string name /*= ""*/)
 | 
			
		|||
		if (dlg->ShowModal() != wxID_OK)
 | 
			
		||||
			return;
 | 
			
		||||
		name = dlg->get_name();
 | 
			
		||||
		if (name == ""){
 | 
			
		||||
		if (name == "") {
 | 
			
		||||
			show_error(this, _(L("The supplied name is empty. It can't be saved.")));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -2745,7 +2751,7 @@ void Page::update_visibility(ConfigOptionMode mode)
 | 
			
		|||
Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const
 | 
			
		||||
{
 | 
			
		||||
	Field* field = nullptr;
 | 
			
		||||
	for (auto opt : m_optgroups){
 | 
			
		||||
	for (auto opt : m_optgroups) {
 | 
			
		||||
		field = opt->get_fieldc(opt_key, opt_index);
 | 
			
		||||
		if (field != nullptr)
 | 
			
		||||
			return field;
 | 
			
		||||
| 
						 | 
				
			
			@ -2753,7 +2759,7 @@ Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*
 | 
			
		|||
	return field;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value){
 | 
			
		||||
bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value) {
 | 
			
		||||
	bool changed = false;
 | 
			
		||||
	for(auto optgroup: m_optgroups) {
 | 
			
		||||
		if (optgroup->set_value(opt_key, value))
 | 
			
		||||
| 
						 | 
				
			
			@ -2789,7 +2795,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
 | 
			
		|||
#else
 | 
			
		||||
		auto tab = GetParent();
 | 
			
		||||
#endif
 | 
			
		||||
	optgroup->m_on_change = [this, tab](t_config_option_key opt_key, boost::any value){
 | 
			
		||||
	optgroup->m_on_change = [this, tab](t_config_option_key opt_key, boost::any value) {
 | 
			
		||||
		//! This function will be called from OptionGroup.
 | 
			
		||||
		//! Using of CallAfter is redundant.
 | 
			
		||||
		//! And in some cases it causes update() function to be recalled again
 | 
			
		||||
| 
						 | 
				
			
			@ -2799,17 +2805,17 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
 | 
			
		|||
//!        });
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	optgroup->m_get_initial_config = [this, tab](){
 | 
			
		||||
	optgroup->m_get_initial_config = [this, tab]() {
 | 
			
		||||
		DynamicPrintConfig config = static_cast<Tab*>(tab)->m_presets->get_selected_preset().config;
 | 
			
		||||
		return config;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	optgroup->m_get_sys_config = [this, tab](){
 | 
			
		||||
	optgroup->m_get_sys_config = [this, tab]() {
 | 
			
		||||
		DynamicPrintConfig config = static_cast<Tab*>(tab)->m_presets->get_selected_preset_parent()->config;
 | 
			
		||||
		return config;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	optgroup->have_sys_config = [this, tab](){
 | 
			
		||||
	optgroup->have_sys_config = [this, tab]() {
 | 
			
		||||
		return static_cast<Tab*>(tab)->m_presets->get_selected_preset_parent() != nullptr;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2850,8 +2856,8 @@ void SavePresetWindow::accept()
 | 
			
		|||
		bool is_unusable_symbol = false;
 | 
			
		||||
		bool is_unusable_postfix = false;
 | 
			
		||||
		const std::string unusable_postfix = PresetCollection::get_suffix_modified();//"(modified)";
 | 
			
		||||
		for (size_t i = 0; i < std::strlen(unusable_symbols); i++){
 | 
			
		||||
			if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){
 | 
			
		||||
		for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
 | 
			
		||||
			if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
 | 
			
		||||
				is_unusable_symbol = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -2863,7 +2869,7 @@ void SavePresetWindow::accept()
 | 
			
		|||
			show_error(this,_(L("The supplied name is not valid;")) + "\n" +
 | 
			
		||||
							_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
 | 
			
		||||
		}
 | 
			
		||||
		else if (is_unusable_postfix){
 | 
			
		||||
		else if (is_unusable_postfix) {
 | 
			
		||||
			show_error(this,_(L("The supplied name is not valid;")) + "\n" +
 | 
			
		||||
							_(L("the following postfix are not allowed:")) + "\n\t" + //unusable_postfix);
 | 
			
		||||
							wxString::FromUTF8(unusable_postfix.c_str()));
 | 
			
		||||
| 
						 | 
				
			
			@ -2896,7 +2902,7 @@ void TabSLAMaterial::build()
 | 
			
		|||
    optgroup->label_width = 190;
 | 
			
		||||
    std::vector<std::string> corrections = { "material_correction_printing", "material_correction_curing" };
 | 
			
		||||
    std::vector<std::string> axes{ "X", "Y", "Z" };
 | 
			
		||||
    for (auto& opt_key : corrections){
 | 
			
		||||
    for (auto& opt_key : corrections) {
 | 
			
		||||
        auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
 | 
			
		||||
        int id = 0;
 | 
			
		||||
        for (auto& axis : axes) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2920,7 +2926,7 @@ void TabSLAMaterial::build()
 | 
			
		|||
    page = add_options_page(_(L("Dependencies")), "wrench.png");
 | 
			
		||||
    optgroup = page->new_optgroup(_(L("Profile dependencies")));
 | 
			
		||||
    Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" };
 | 
			
		||||
    line.widget = [this](wxWindow* parent){
 | 
			
		||||
    line.widget = [this](wxWindow* parent) {
 | 
			
		||||
        return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
 | 
			
		||||
    };
 | 
			
		||||
    optgroup->append_line(line, &m_colored_Label);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ public:
 | 
			
		|||
        m_item_color = &wxGetApp().get_label_clr_default();
 | 
			
		||||
		SetSizer(m_vsizer);
 | 
			
		||||
	}
 | 
			
		||||
	~Page(){}
 | 
			
		||||
	~Page() {}
 | 
			
		||||
 | 
			
		||||
	bool				m_is_modified_values{ false };
 | 
			
		||||
	bool				m_is_nonsys_values{ true };
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +205,7 @@ public:
 | 
			
		|||
        set_type();
 | 
			
		||||
		wxGetApp().tabs_list.push_back(this);
 | 
			
		||||
	}
 | 
			
		||||
	~Tab(){
 | 
			
		||||
	~Tab() {
 | 
			
		||||
		wxGetApp().delete_tab_from_list(this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +245,7 @@ public:
 | 
			
		|||
	PageShp		add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false);
 | 
			
		||||
 | 
			
		||||
	virtual void	OnActivate();
 | 
			
		||||
	virtual void	on_preset_loaded(){}
 | 
			
		||||
	virtual void	on_preset_loaded() {}
 | 
			
		||||
	virtual void	build() = 0;
 | 
			
		||||
	virtual void	update() = 0;
 | 
			
		||||
	virtual void	init_options_list();
 | 
			
		||||
| 
						 | 
				
			
			@ -283,7 +283,7 @@ public:
 | 
			
		|||
	TabPrint() {}
 | 
			
		||||
	TabPrint(wxNotebook* parent) : 
 | 
			
		||||
		Tab(parent, _(L("Print Settings")), "print") {}
 | 
			
		||||
	~TabPrint(){}
 | 
			
		||||
	~TabPrint() {}
 | 
			
		||||
 | 
			
		||||
	ogStaticText*	m_recommended_thin_wall_thickness_description_line;
 | 
			
		||||
	bool		m_support_material_overhangs_queried = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +304,7 @@ public:
 | 
			
		|||
	TabFilament() {}
 | 
			
		||||
	TabFilament(wxNotebook* parent) : 
 | 
			
		||||
		Tab(parent, _(L("Filament Settings")), "filament") {}
 | 
			
		||||
	~TabFilament(){}
 | 
			
		||||
	~TabFilament() {}
 | 
			
		||||
 | 
			
		||||
	void		build() override;
 | 
			
		||||
	void		reload_config() override;
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +337,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	TabPrinter() {}
 | 
			
		||||
	TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), "printer") {}
 | 
			
		||||
	~TabPrinter(){}
 | 
			
		||||
	~TabPrinter() {}
 | 
			
		||||
 | 
			
		||||
	void		build() override;
 | 
			
		||||
    void		build_fff();
 | 
			
		||||
| 
						 | 
				
			
			@ -361,7 +361,7 @@ public:
 | 
			
		|||
    TabSLAMaterial() {}
 | 
			
		||||
    TabSLAMaterial(wxNotebook* parent) :
 | 
			
		||||
		Tab(parent, _(L("SLA Material Settings")), "sla_material") {}
 | 
			
		||||
    ~TabSLAMaterial(){}
 | 
			
		||||
    ~TabSLAMaterial() {}
 | 
			
		||||
 | 
			
		||||
	void		build() override;
 | 
			
		||||
	void		update() override;
 | 
			
		||||
| 
						 | 
				
			
			@ -372,8 +372,8 @@ public:
 | 
			
		|||
class SavePresetWindow :public wxDialog
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _(L("Save preset"))){}
 | 
			
		||||
	~SavePresetWindow(){}
 | 
			
		||||
	SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _(L("Save preset"))) {}
 | 
			
		||||
	~SavePresetWindow() {}
 | 
			
		||||
 | 
			
		||||
	std::string		m_chosen_name;
 | 
			
		||||
	wxComboBox*		m_combo;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -534,7 +534,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &
 | 
			
		|||
    // Add instance nodes
 | 
			
		||||
    PrusaObjectDataViewModelNode *instance_node = nullptr;    
 | 
			
		||||
    size_t counter = 0;
 | 
			
		||||
    while (counter < num){
 | 
			
		||||
    while (counter < num) {
 | 
			
		||||
        instance_node = new PrusaObjectDataViewModelNode(inst_root_node, itInstance);
 | 
			
		||||
        inst_root_node->Append(instance_node);
 | 
			
		||||
        // notify control
 | 
			
		||||
| 
						 | 
				
			
			@ -559,11 +559,11 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
 | 
			
		|||
	// first remove the node from the parent's array of children;
 | 
			
		||||
	// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
 | 
			
		||||
	//       thus removing the node from it doesn't result in freeing it
 | 
			
		||||
	if (node_parent){
 | 
			
		||||
	if (node_parent) {
 | 
			
		||||
		auto id = node_parent->GetChildren().Index(node);
 | 
			
		||||
        auto idx = node->GetIdx();
 | 
			
		||||
		node_parent->GetChildren().Remove(node);
 | 
			
		||||
		if (id > 0){ 
 | 
			
		||||
		if (id > 0) { 
 | 
			
		||||
			if(id == node_parent->GetChildCount()) id--;
 | 
			
		||||
			ret_item = wxDataViewItem(node_parent->GetChildren().Item(id));
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -607,7 +607,7 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
 | 
			
		|||
		auto id = it - m_objects.begin();
 | 
			
		||||
		if (it != m_objects.end())
 | 
			
		||||
			m_objects.erase(it);
 | 
			
		||||
		if (id > 0){ 
 | 
			
		||||
		if (id > 0) { 
 | 
			
		||||
			if(id == m_objects.size()) id--;
 | 
			
		||||
			ret_item = wxDataViewItem(m_objects[id]);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -862,7 +862,7 @@ wxDataViewItem PrusaObjectDataViewModel::MoveChildUp(const wxDataViewItem &item)
 | 
			
		|||
		return ret_item;
 | 
			
		||||
 | 
			
		||||
	auto volume_id = node->GetVolumeId();
 | 
			
		||||
	if (0 < volume_id && volume_id < node_parent->GetChildCount()){
 | 
			
		||||
	if (0 < volume_id && volume_id < node_parent->GetChildCount()) {
 | 
			
		||||
		node_parent->SwapChildrens(volume_id - 1, volume_id);
 | 
			
		||||
		ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id - 1));
 | 
			
		||||
		ItemChanged(item);
 | 
			
		||||
| 
						 | 
				
			
			@ -886,7 +886,7 @@ wxDataViewItem PrusaObjectDataViewModel::MoveChildDown(const wxDataViewItem &ite
 | 
			
		|||
		return ret_item;
 | 
			
		||||
 | 
			
		||||
	auto volume_id = node->GetVolumeId();
 | 
			
		||||
	if (0 <= volume_id && volume_id+1 < node_parent->GetChildCount()){
 | 
			
		||||
	if (0 <= volume_id && volume_id+1 < node_parent->GetChildCount()) {
 | 
			
		||||
		node_parent->SwapChildrens(volume_id + 1, volume_id);
 | 
			
		||||
		ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id + 1));
 | 
			
		||||
		ItemChanged(item);
 | 
			
		||||
| 
						 | 
				
			
			@ -1586,7 +1586,7 @@ void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event)
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    m_is_left_down = true;
 | 
			
		||||
    if (is_point_in_rect(pos, m_rect_one_layer_icon)){
 | 
			
		||||
    if (is_point_in_rect(pos, m_rect_one_layer_icon)) {
 | 
			
		||||
        m_is_one_layer = !m_is_one_layer;
 | 
			
		||||
        m_selection == ssLower ? correct_lower_value() : correct_higher_value();
 | 
			
		||||
        if (!m_selection) m_selection = ssHigher;
 | 
			
		||||
| 
						 | 
				
			
			@ -1626,10 +1626,10 @@ void PrusaDoubleSlider::OnMotion(wxMouseEvent& event)
 | 
			
		|||
    const wxClientDC dc(this);
 | 
			
		||||
    const wxPoint pos = event.GetLogicalPosition(dc);
 | 
			
		||||
    m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon);
 | 
			
		||||
    if (!m_is_left_down && !m_is_one_layer){
 | 
			
		||||
    if (!m_is_left_down && !m_is_one_layer) {
 | 
			
		||||
        m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action);
 | 
			
		||||
    }
 | 
			
		||||
    else if (m_is_left_down || m_is_right_down){
 | 
			
		||||
    else if (m_is_left_down || m_is_right_down) {
 | 
			
		||||
        if (m_selection == ssLower) {
 | 
			
		||||
            m_lower_value = get_value_from_position(pos.x, pos.y);
 | 
			
		||||
            correct_lower_value();
 | 
			
		||||
| 
						 | 
				
			
			@ -1741,7 +1741,7 @@ void PrusaDoubleSlider::OnKeyDown(wxKeyEvent &event)
 | 
			
		|||
    {
 | 
			
		||||
        if (key == WXK_LEFT || key == WXK_RIGHT)
 | 
			
		||||
            move_current_thumb(key == WXK_LEFT); 
 | 
			
		||||
        else if (key == WXK_UP || key == WXK_DOWN){
 | 
			
		||||
        else if (key == WXK_UP || key == WXK_DOWN) {
 | 
			
		||||
            m_selection = key == WXK_UP ? ssHigher : ssLower;
 | 
			
		||||
            Refresh();
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -638,7 +638,7 @@ public:
 | 
			
		|||
        long style = wxSL_VERTICAL,
 | 
			
		||||
        const wxValidator& val = wxDefaultValidator,
 | 
			
		||||
        const wxString& name = wxEmptyString);
 | 
			
		||||
    ~PrusaDoubleSlider(){}
 | 
			
		||||
    ~PrusaDoubleSlider() {}
 | 
			
		||||
 | 
			
		||||
    int GetLowerValue() const {
 | 
			
		||||
        return m_lower_value;
 | 
			
		||||
| 
						 | 
				
			
			@ -661,12 +661,12 @@ public:
 | 
			
		|||
    }
 | 
			
		||||
    void ChangeOneLayerLock();
 | 
			
		||||
 | 
			
		||||
    void OnPaint(wxPaintEvent& ){ render();}
 | 
			
		||||
    void OnPaint(wxPaintEvent& ) { render();}
 | 
			
		||||
    void OnLeftDown(wxMouseEvent& event);
 | 
			
		||||
    void OnMotion(wxMouseEvent& event);
 | 
			
		||||
    void OnLeftUp(wxMouseEvent& event);
 | 
			
		||||
    void OnEnterWin(wxMouseEvent& event){ enter_window(event, true); }
 | 
			
		||||
    void OnLeaveWin(wxMouseEvent& event){ enter_window(event, false); }
 | 
			
		||||
    void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); }
 | 
			
		||||
    void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); }
 | 
			
		||||
    void OnWheel(wxMouseEvent& event);
 | 
			
		||||
    void OnKeyDown(wxKeyEvent &event);
 | 
			
		||||
    void OnKeyUp(wxKeyEvent &event);
 | 
			
		||||
| 
						 | 
				
			
			@ -770,11 +770,11 @@ public:
 | 
			
		|||
        wxWindowID id,
 | 
			
		||||
        const wxPoint& pos = wxDefaultPosition,
 | 
			
		||||
        const wxSize& size = wxDefaultSize);
 | 
			
		||||
    ~PrusaLockButton(){}
 | 
			
		||||
    ~PrusaLockButton() {}
 | 
			
		||||
 | 
			
		||||
    void    OnButton(wxCommandEvent& event);
 | 
			
		||||
    void    OnEnterBtn(wxMouseEvent& event){ enter_button(true); event.Skip(); }
 | 
			
		||||
    void    OnLeaveBtn(wxMouseEvent& event){ enter_button(false); event.Skip(); }
 | 
			
		||||
    void    OnEnterBtn(wxMouseEvent& event) { enter_button(true); event.Skip(); }
 | 
			
		||||
    void    OnLeaveBtn(wxMouseEvent& event) { enter_button(false); event.Skip(); }
 | 
			
		||||
 | 
			
		||||
    bool    IsLocked() const { return m_is_pushed; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue