mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 15:44:12 -06:00
Merge remote-tracking branch 'remotes/origin/vb_wold_object_manipulation'
This commit is contained in:
commit
a351e99bac
34 changed files with 1269 additions and 861 deletions
|
@ -280,7 +280,7 @@ std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, s
|
|||
|
||||
// right: option description
|
||||
std::string descr = def.tooltip;
|
||||
if (show_defaults && def.default_value != nullptr && def.type != coBool
|
||||
if (show_defaults && def.default_value && def.type != coBool
|
||||
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
||||
descr += " (";
|
||||
if (!def.sidetext.empty()) {
|
||||
|
@ -627,7 +627,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
|
|||
// Let the parent decide what to do if the opt_key is not defined by this->def().
|
||||
return nullptr;
|
||||
ConfigOption *opt = nullptr;
|
||||
if (optdef->default_value != nullptr) {
|
||||
if (optdef->default_value) {
|
||||
opt = (optdef->default_value->type() == coEnum) ?
|
||||
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
|
||||
new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) :
|
||||
|
@ -783,8 +783,8 @@ void StaticConfig::set_defaults()
|
|||
for (const std::string &key : this->keys()) {
|
||||
const ConfigOptionDef *def = defs->get(key);
|
||||
ConfigOption *opt = this->option(key);
|
||||
if (def != nullptr && opt != nullptr && def->default_value != nullptr)
|
||||
opt->set(def->default_value);
|
||||
if (def != nullptr && opt != nullptr && def->default_value)
|
||||
opt->set(def->default_value.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include "libslic3r.h"
|
||||
#include "clonable_ptr.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
@ -1010,7 +1011,10 @@ public:
|
|||
// What type? bool, int, string etc.
|
||||
ConfigOptionType type = coNone;
|
||||
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
|
||||
const ConfigOption *default_value = nullptr;
|
||||
Slic3r::clonable_ptr<const ConfigOption> default_value;
|
||||
void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
|
||||
template<typename T>
|
||||
const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
|
||||
|
||||
// Usually empty.
|
||||
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
|
||||
|
@ -1099,12 +1103,6 @@ typedef std::map<t_config_option_key, ConfigOptionDef> t_optiondef_map;
|
|||
class ConfigDef
|
||||
{
|
||||
public:
|
||||
~ConfigDef() {
|
||||
for (std::pair<const t_config_option_key, ConfigOptionDef> &def : this->options)
|
||||
delete def.second.default_value;
|
||||
this->options.clear();
|
||||
}
|
||||
|
||||
t_optiondef_map options;
|
||||
|
||||
bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; }
|
||||
|
|
|
@ -269,6 +269,21 @@ extern Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec
|
|||
// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis.
|
||||
extern double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to);
|
||||
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
inline bool is_rotation_ninety_degrees(double a)
|
||||
{
|
||||
a = fmod(std::abs(a), 0.5 * M_PI);
|
||||
if (a > 0.25 * PI)
|
||||
a = 0.5 * PI - a;
|
||||
return a < 0.001;
|
||||
}
|
||||
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
||||
{
|
||||
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,19 @@ unsigned int Model::s_auto_extruder_id = 1;
|
|||
|
||||
size_t ModelBase::s_last_id = 0;
|
||||
|
||||
// Unique object / instance ID for the wipe tower.
|
||||
ModelID wipe_tower_object_id()
|
||||
{
|
||||
static ModelBase mine;
|
||||
return mine.id();
|
||||
}
|
||||
|
||||
ModelID wipe_tower_instance_id()
|
||||
{
|
||||
static ModelBase mine;
|
||||
return mine.id();
|
||||
}
|
||||
|
||||
Model& Model::assign_copy(const Model &rhs)
|
||||
{
|
||||
this->copy_id(rhs);
|
||||
|
@ -1320,6 +1333,58 @@ void ModelObject::repair()
|
|||
v->mesh.repair();
|
||||
}
|
||||
|
||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
|
||||
// This situation is solved by baking in the instance transformation into the mesh vertices.
|
||||
// Rotation and mirroring is being baked in. In case the instance scaling was non-uniform, it is baked in as well.
|
||||
void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
|
||||
{
|
||||
assert(instance_idx < this->instances.size());
|
||||
|
||||
const Geometry::Transformation reference_trafo = this->instances[instance_idx]->get_transformation();
|
||||
if (Geometry::is_rotation_ninety_degrees(reference_trafo.get_rotation()))
|
||||
// nothing to do, scaling in the world coordinate space is possible in the representation of Geometry::Transformation.
|
||||
return;
|
||||
|
||||
bool left_handed = reference_trafo.is_left_handed();
|
||||
bool has_mirrorring = ! reference_trafo.get_mirror().isApprox(Vec3d(1., 1., 1.));
|
||||
bool uniform_scaling = std::abs(reference_trafo.get_scaling_factor().x() - reference_trafo.get_scaling_factor().y()) < EPSILON &&
|
||||
std::abs(reference_trafo.get_scaling_factor().x() - reference_trafo.get_scaling_factor().z()) < EPSILON;
|
||||
double new_scaling_factor = uniform_scaling ? reference_trafo.get_scaling_factor().x() : 1.;
|
||||
|
||||
// Adjust the instances.
|
||||
for (size_t i = 0; i < this->instances.size(); ++ i) {
|
||||
ModelInstance &model_instance = *this->instances[i];
|
||||
model_instance.set_rotation(Vec3d(0., 0., Geometry::rotation_diff_z(reference_trafo.get_rotation(), model_instance.get_rotation())));
|
||||
model_instance.set_scaling_factor(Vec3d(new_scaling_factor, new_scaling_factor, new_scaling_factor));
|
||||
model_instance.set_mirror(Vec3d(1., 1., 1.));
|
||||
}
|
||||
|
||||
// Adjust the meshes.
|
||||
// Transformation to be applied to the meshes.
|
||||
Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
|
||||
Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
|
||||
for (ModelVolume *model_volume : this->volumes) {
|
||||
const Geometry::Transformation volume_trafo = model_volume->get_transformation();
|
||||
bool volume_left_handed = volume_trafo.is_left_handed();
|
||||
bool volume_has_mirrorring = ! volume_trafo.get_mirror().isApprox(Vec3d(1., 1., 1.));
|
||||
bool volume_uniform_scaling = std::abs(volume_trafo.get_scaling_factor().x() - volume_trafo.get_scaling_factor().y()) < EPSILON &&
|
||||
std::abs(volume_trafo.get_scaling_factor().x() - volume_trafo.get_scaling_factor().z()) < EPSILON;
|
||||
double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.;
|
||||
// Transform the mesh.
|
||||
Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0);
|
||||
model_volume->transform_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed);
|
||||
// Reset the rotation, scaling and mirroring.
|
||||
model_volume->set_rotation(Vec3d(0., 0., 0.));
|
||||
model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor));
|
||||
model_volume->set_mirror(Vec3d(1., 1., 1.));
|
||||
// Move the reference point of the volume to compensate for the change of the instance trafo.
|
||||
model_volume->set_offset(volume_offset_correction * volume_trafo.get_offset());
|
||||
}
|
||||
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
double ModelObject::get_min_z() const
|
||||
{
|
||||
if (instances.empty())
|
||||
|
@ -1708,6 +1773,22 @@ void ModelVolume::scale_geometry(const Vec3d& versor)
|
|||
m_convex_hull.scale(versor);
|
||||
}
|
||||
|
||||
void ModelVolume::transform_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
|
||||
{
|
||||
this->mesh.transform(mesh_trafo, fix_left_handed);
|
||||
this->m_convex_hull.transform(mesh_trafo, fix_left_handed);
|
||||
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
||||
this->set_new_unique_id();
|
||||
}
|
||||
|
||||
void ModelVolume::transform_mesh(const Matrix3d &matrix, bool fix_left_handed)
|
||||
{
|
||||
this->mesh.transform(matrix, fix_left_handed);
|
||||
this->m_convex_hull.transform(matrix, fix_left_handed);
|
||||
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
||||
this->set_new_unique_id();
|
||||
}
|
||||
|
||||
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
|
||||
{
|
||||
mesh->transform(get_matrix(dont_translate));
|
||||
|
|
|
@ -54,6 +54,10 @@ struct ModelID
|
|||
size_t id;
|
||||
};
|
||||
|
||||
// Unique object / instance ID for the wipe tower.
|
||||
extern ModelID wipe_tower_object_id();
|
||||
extern ModelID wipe_tower_instance_id();
|
||||
|
||||
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
|
||||
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
|
||||
// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ModelBase derived instances
|
||||
|
@ -85,6 +89,9 @@ private:
|
|||
|
||||
static inline ModelID generate_new_id() { return ModelID(++ s_last_id); }
|
||||
static size_t s_last_id;
|
||||
|
||||
friend ModelID wipe_tower_object_id();
|
||||
friend ModelID wipe_tower_instance_id();
|
||||
};
|
||||
|
||||
#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
|
||||
|
@ -265,6 +272,11 @@ public:
|
|||
ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates
|
||||
void split(ModelObjectPtrs* new_objects);
|
||||
void repair();
|
||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
|
||||
// This situation is solved by baking in the instance transformation into the mesh vertices.
|
||||
// Rotation and mirroring is being baked in. In case the instance scaling was non-uniform, it is baked in as well.
|
||||
void bake_xy_rotation_into_meshes(size_t instance_idx);
|
||||
|
||||
double get_min_z() const;
|
||||
double get_instance_min_z(size_t instance_idx) const;
|
||||
|
@ -421,6 +433,8 @@ protected:
|
|||
|
||||
explicit ModelVolume(const ModelVolume &rhs) = default;
|
||||
void set_model_object(ModelObject *model_object) { object = model_object; }
|
||||
void transform_mesh(const Transform3d& t, bool fix_left_handed);
|
||||
void transform_mesh(const Matrix3d& m, bool fix_left_handed);
|
||||
|
||||
private:
|
||||
// Parent object owning this ModelVolume.
|
||||
|
|
|
@ -40,6 +40,11 @@ typedef std::vector<Vec3crd> Points3;
|
|||
typedef std::vector<Vec2d> Pointfs;
|
||||
typedef std::vector<Vec3d> Pointf3s;
|
||||
|
||||
typedef Eigen::Matrix<float, 2, 2, Eigen::DontAlign> Matrix2f;
|
||||
typedef Eigen::Matrix<double, 2, 2, Eigen::DontAlign> Matrix2d;
|
||||
typedef Eigen::Matrix<float, 3, 3, Eigen::DontAlign> Matrix3f;
|
||||
typedef Eigen::Matrix<double, 3, 3, Eigen::DontAlign> Matrix3d;
|
||||
|
||||
typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f;
|
||||
typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d;
|
||||
typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -307,8 +307,8 @@ protected:
|
|||
m_keys.emplace_back(kvp.first);
|
||||
const ConfigOptionDef *def = defs->get(kvp.first);
|
||||
assert(def != nullptr);
|
||||
if (def->default_value != nullptr)
|
||||
opt->set(def->default_value);
|
||||
if (def->default_value)
|
||||
opt->set(def->default_value.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -330,6 +330,17 @@ void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
|
|||
}
|
||||
}
|
||||
|
||||
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
|
||||
{
|
||||
stl_transform(&stl, m);
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
if (fix_left_handed && m.determinant() < 0.) {
|
||||
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
|
||||
this->repair();
|
||||
stl_reverse_all_facets(&stl);
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleMesh::align_to_origin()
|
||||
{
|
||||
this->translate(
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
void mirror_y() { this->mirror(Y); }
|
||||
void mirror_z() { this->mirror(Z); }
|
||||
void transform(const Transform3d& t, bool fix_left_handed = false);
|
||||
void transform(const Matrix3d& t, bool fix_left_handed = false);
|
||||
void align_to_origin();
|
||||
void rotate(double angle, Point* center);
|
||||
TriangleMeshPtrs split() const;
|
||||
|
|
168
src/libslic3r/clonable_ptr.hpp
Normal file
168
src/libslic3r/clonable_ptr.hpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
// clonable_ptr: a smart pointer with a usage similar to unique_ptr, with the exception, that
|
||||
// the copy constructor / copy assignment operator work by calling the ->clone() method.
|
||||
|
||||
// derived from https://github.com/SRombauts/shared_ptr/blob/master/include/unique_ptr.hpp
|
||||
/**
|
||||
* @file clonable_ptr.hpp
|
||||
* @brief clonable_ptr is a fake implementation to use in place of a C++11 std::clonable_ptr when compiling on an older compiler.
|
||||
*
|
||||
* @see http://www.cplusplus.com/reference/memory/clonable_ptr/
|
||||
*
|
||||
* Copyright (c) 2014-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
*
|
||||
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
* or copy at http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Detect whether the compiler supports C++11 noexcept exception specifications.
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900 && ! defined(noexcept)
|
||||
#define noexcept throw()
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
class clonable_ptr
|
||||
{
|
||||
public:
|
||||
/// The type of the managed object, aliased as member type
|
||||
typedef T element_type;
|
||||
|
||||
/// @brief Default constructor
|
||||
clonable_ptr() noexcept :
|
||||
px(nullptr)
|
||||
{
|
||||
}
|
||||
/// @brief Constructor with the provided pointer to manage
|
||||
explicit clonable_ptr(T* p) noexcept :
|
||||
px(p)
|
||||
{
|
||||
}
|
||||
/// @brief Copy constructor, clones by calling the rhs.clone() method
|
||||
clonable_ptr(const clonable_ptr& rhs) :
|
||||
px(rhs ? rhs.px->clone() : nullptr)
|
||||
{
|
||||
}
|
||||
/// @brief Move constructor, never throws
|
||||
clonable_ptr(clonable_ptr&& rhs) noexcept :
|
||||
px(rhs.px)
|
||||
{
|
||||
rhs.px = nullptr;
|
||||
}
|
||||
/// @brief Assignment operator
|
||||
clonable_ptr& operator=(const clonable_ptr& rhs)
|
||||
{
|
||||
delete px;
|
||||
px = rhs ? rhs.px->clone() : nullptr;
|
||||
return *this;
|
||||
}
|
||||
/// @brief Move operator, never throws
|
||||
clonable_ptr& operator=(clonable_ptr&& rhs)
|
||||
{
|
||||
delete px;
|
||||
px = rhs.px;
|
||||
rhs.px = nullptr;
|
||||
return *this;
|
||||
}
|
||||
/// @brief the destructor releases its ownership and destroy the object
|
||||
inline ~clonable_ptr() noexcept
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
/// @brief this reset releases its ownership and destroy the object
|
||||
inline void reset() noexcept
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
/// @brief this reset release its ownership and re-acquire another one
|
||||
void reset(T* p) noexcept
|
||||
{
|
||||
assert((nullptr == p) || (px != p)); // auto-reset not allowed
|
||||
destroy();
|
||||
px = p;
|
||||
}
|
||||
|
||||
/// @brief Swap method for the copy-and-swap idiom (copy constructor and swap method)
|
||||
void swap(clonable_ptr& rhs) noexcept
|
||||
{
|
||||
T *tmp = px;
|
||||
px = rhs.px;
|
||||
rhs.px = tmp;
|
||||
}
|
||||
|
||||
/// @brief release the ownership of the px pointer without destroying the object!
|
||||
inline void release() noexcept
|
||||
{
|
||||
px = nullptr;
|
||||
}
|
||||
|
||||
// reference counter operations :
|
||||
inline operator bool() const noexcept
|
||||
{
|
||||
return (nullptr != px); // TODO nullptrptr
|
||||
}
|
||||
|
||||
// underlying pointer operations :
|
||||
inline T& operator*() const noexcept
|
||||
{
|
||||
assert(nullptr != px);
|
||||
return *px;
|
||||
}
|
||||
inline T* operator->() const noexcept
|
||||
{
|
||||
assert(nullptr != px);
|
||||
return px;
|
||||
}
|
||||
inline T* get() const noexcept
|
||||
{
|
||||
// no assert, can return nullptr
|
||||
return px;
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief release the ownership of the px pointer and destroy the object
|
||||
inline void destroy() noexcept
|
||||
{
|
||||
delete px;
|
||||
px = nullptr;
|
||||
}
|
||||
|
||||
/// @brief hack: const-cast release the ownership of the px pointer without destroying the object!
|
||||
inline void release() const noexcept
|
||||
{
|
||||
px = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* px; //!< Native pointer
|
||||
};
|
||||
|
||||
// comparison operators
|
||||
template<class T, class U> inline bool operator==(const clonable_ptr<T>& l, const clonable_ptr<U>& r) noexcept
|
||||
{
|
||||
return (l.get() == r.get());
|
||||
}
|
||||
template<class T, class U> inline bool operator!=(const clonable_ptr<T>& l, const clonable_ptr<U>& r) noexcept
|
||||
{
|
||||
return (l.get() != r.get());
|
||||
}
|
||||
template<class T, class U> inline bool operator<=(const clonable_ptr<T>& l, const clonable_ptr<U>& r) noexcept
|
||||
{
|
||||
return (l.get() <= r.get());
|
||||
}
|
||||
template<class T, class U> inline bool operator<(const clonable_ptr<T>& l, const clonable_ptr<U>& r) noexcept
|
||||
{
|
||||
return (l.get() < r.get());
|
||||
}
|
||||
template<class T, class U> inline bool operator>=(const clonable_ptr<T>& l, const clonable_ptr<U>& r) noexcept
|
||||
{
|
||||
return (l.get() >= r.get());
|
||||
}
|
||||
template<class T, class U> inline bool operator>(const clonable_ptr<T>& l, const clonable_ptr<U>& r) noexcept
|
||||
{
|
||||
return (l.get() > r.get());
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
Loading…
Add table
Add a link
Reference in a new issue