Cut WIP: Send to the cut() whole cut_matrix instead of cut_plane_pos and rotation angles

+ Fixed units inside input window
+ NotificationManager: Added info line for loaded object with cut parts
+ Next Code refactoring
This commit is contained in:
YuSanka 2022-09-26 17:23:40 +02:00
parent b2d5fd72e9
commit 66e2c3b30a
7 changed files with 325 additions and 302 deletions

View File

@ -1381,11 +1381,13 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn
return connector_mesh;
}
void ModelObject::apply_cut_connectors(const std::string& name)
void ModelObject::apply_cut_connectors(const std::string& new_name)
{
if (cut_connectors.empty())
return;
using namespace Geometry;
size_t connector_id = cut_id.connectors_cnt();
for (const CutConnector& connector : cut_connectors) {
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
@ -1393,15 +1395,11 @@ void ModelObject::apply_cut_connectors(const std::string& name)
ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
// Transform the new modifier to be aligned inside the instance
new_volume->set_transformation(Geometry::assemble_transform(
connector.pos,
connector.rotation,
Vec3d(connector.radius, connector.radius, connector.height),
Vec3d::Ones()
));
new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m *
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast<double>()));
new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
new_volume->name = name + "-" + std::to_string(++connector_id);
new_volume->name = new_name + "-" + std::to_string(++connector_id);
}
cut_id.increase_connectors_cnt(cut_connectors.size());
@ -1426,14 +1424,8 @@ void ModelObject::synchronize_model_after_cut()
}
}
ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes)
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
{
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
return {};
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// initiate/update cut attributes for object
if (cut_id.id().invalid())
cut_id.init();
{
@ -1444,8 +1436,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
if (cut_obj_cnt > 0)
cut_id.increase_check_sum(size_t(cut_obj_cnt));
}
}
auto clone_obj = [this](ModelObject** obj) {
void ModelObject::clone_for_cut(ModelObject** obj)
{
(*obj) = ModelObject::new_clone(*this);
(*obj)->set_model(nullptr);
(*obj)->sla_support_points.clear();
@ -1453,46 +1447,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
(*obj)->sla_points_status = sla::PointsStatus::NoPoints;
(*obj)->clear_volumes();
(*obj)->input_file.clear();
};
}
// Clone the object to duplicate instances, materials etc.
ModelObject* upper{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
clone_obj(&upper);
void ModelVolume::apply_tolerance()
{
if (!cut_info.is_connector)
return;
ModelObject* lower{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepLower))
clone_obj(&lower);
std::vector<ModelObject*> dowels;
using namespace Geometry;
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)),
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
const auto cut_matrix = rotation_transform(cut_rotation).inverse() * assemble_transform(-cut_center);
const auto invert_cut_matrix = assemble_transform(cut_center, cut_rotation);
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX();
auto apply_tolerance = [](ModelVolume * vol)
{
Vec3d sf = vol->get_scaling_factor();
Vec3d sf = get_scaling_factor();
/*
// correct Z offset in respect to the new size
Vec3d pos = vol->get_offset();
@ -1500,37 +1462,35 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
vol->set_offset(pos);
*/
// make a "hole" wider
sf[X] *= (1 + vol->cut_info.radius_tolerance);
sf[Y] *= (1 + vol->cut_info.radius_tolerance);
sf[X] *= 1. + double(cut_info.radius_tolerance);
sf[Y] *= 1. + double(cut_info.radius_tolerance);
// make a "hole" dipper
sf[Z] *= (1 + vol->cut_info.height_tolerance);
vol->set_scaling_factor(sf);
};
sf[Z] *= 1. + double(cut_info.height_tolerance);
for (ModelVolume* volume : volumes) {
const auto volume_matrix = volume->get_matrix();
set_scaling_factor(sf);
}
volume->supported_facets.reset();
volume->seam_facets.reset();
volume->mmu_segmentation_facets.reset();
if (!volume->is_model_part()) {
if (volume->cut_info.is_connector) {
void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace)
{
volume->cut_info.discard();
const auto volume_matrix = volume->get_matrix();
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume* vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
apply_tolerance(vol);
vol->apply_tolerance();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume* vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
if (volume->cut_info.connector_type == CutConnectorType::Dowel)
apply_tolerance(vol);
vol->apply_tolerance();
else
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
@ -1539,7 +1499,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject* dowel{ nullptr };
// Clone the object to duplicate instances, materials etc.
clone_obj(&dowel);
clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume* vol = dowel->add_volume(*volume);
@ -1554,26 +1514,56 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
dowels.push_back(dowel);
}
}
else {
}
void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
{
const auto volume_matrix = volume->get_matrix();
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(cut_matrix * volume->get_matrix());
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut))
lower->add_volume(*volume);
}
}
else if (!volume->mesh().empty()) {
}
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix)
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume* vol = object->add_volume(mesh);
vol->name = src_volume->name;
// Don't copy the config's ID.
vol->config.assign_config(src_volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != src_volume->config.id());
vol->set_material(src_volume->material_id(), *src_volume->material());
}
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace)
{
const auto volume_matrix = volume->get_matrix();
using namespace Geometry;
const Transformation cut_transformation = Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1 * cut_transformation.get_offset());
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(cut_matrix * instance_matrix* volume_matrix, true);
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
volume->reset_mesh();
// Reset volume transformation except for offset
@ -1582,6 +1572,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
volume->set_offset(offset);
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
@ -1592,144 +1583,168 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
lower_mesh = TriangleMesh(lower_its);
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) {
upper_mesh.transform(invert_cut_matrix);
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
lower_mesh.transform(invert_cut_matrix);
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
// The upper part displacement is set to half of the lower part bounding box
// this is done in hope at least a part of the upper part will always be visible and draggable
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
}
}
static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance)
{
if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) {
object->center_around_origin();
object->translate_instances(-object->origin_translation);
object->origin_translation = Vec3d::Zero();
}
else {
object->invalidate_bounding_box();
object->center_around_origin();
}
}
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero())
{
using namespace Geometry;
static Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX();
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < object->instances.size(); ++i) {
auto& obj_instance = object->instances[i];
const Vec3d offset = obj_instance->get_offset();
const double rot_z = obj_instance->get_rotation().z();
obj_instance->set_transformation(Transformation());
const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() :
assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace;
obj_instance->set_offset(offset + displace);
Vec3d rotation = Vec3d::Zero();
if (!flip && !place_on_cut) {
if ( i != src_instance_idx)
rotation[Z] = rot_z;
}
else {
Transform3d rotation_matrix = Transform3d::Identity();
if (flip)
rotation_matrix = rotation_transform(rotate_z180);
if (place_on_cut)
rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse();
if (i != src_instance_idx)
rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix;
rotation = Transformation(rotation_matrix).get_rotation();
}
obj_instance->set_rotation(rotation);
}
}
ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
{
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
return {};
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// apply cut attributes for object
apply_cut_attributes(attributes);
// Clone the object to duplicate instances, materials etc.
ModelObject* upper{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
clone_for_cut(&upper);
ModelObject* lower{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepLower))
clone_for_cut(&lower);
std::vector<ModelObject*> dowels;
using namespace Geometry;
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation(),
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
const Transformation cut_transformation = Transformation(cut_matrix);
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1. * cut_transformation.get_offset());
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
for (ModelVolume* volume : volumes) {
volume->supported_facets.reset();
volume->seam_facets.reset();
volume->mmu_segmentation_facets.reset();
if (!volume->is_model_part()) {
if (volume->cut_info.is_connector)
process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
else
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
}
else if (!volume->mesh().empty())
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace);
}
// Post-process cut parts
ModelObjectPtrs res;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
upper->center_around_origin();
upper->translate_instances(-upper->origin_translation);
upper->origin_translation = Vec3d::Zero();
}
else {
upper->invalidate_bounding_box();
upper->center_around_origin();
}
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); ++i) {
auto& obj_instance = upper->instances[i];
const Vec3d offset = obj_instance->get_offset();
const double rot_z = obj_instance->get_rotation().z();
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace;
invalidate_translations(upper, instances[instance]);
obj_instance->set_transformation(Geometry::Transformation());
obj_instance->set_offset(offset + displace);
Vec3d rotation = Vec3d::Zero();
if (attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) {
Transform3d trafo = rotation_transform(cut_rotation).inverse();
if (i != instance)
trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo;
rotation = Transformation(trafo).get_rotation();
}
else if (attributes.has(ModelObjectCutAttribute::FlipUpper)) {
rotation = rotate_z180;
if (i != instance)
rotation[Z] = rot_z;
}
else if (i != instance)
rotation[Z] = rot_z/* * Vec3d::UnitZ()*/;
obj_instance->set_rotation(rotation);
}
reset_instance_transformation(upper, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper),
local_displace);
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
lower->center_around_origin();
lower->translate_instances(-lower->origin_translation);
lower->origin_translation = Vec3d::Zero();
}
else {
lower->invalidate_bounding_box();
lower->center_around_origin();
}
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); ++i) {
auto& obj_instance = lower->instances[i];
const Vec3d offset = obj_instance->get_offset();
const double rot_z = obj_instance->get_rotation().z();
obj_instance->set_transformation(Geometry::Transformation());
obj_instance->set_offset(offset);
invalidate_translations(lower, instances[instance]);
Vec3d rotation = Vec3d::Zero();
if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) {
Transform3d trafo = rotation_transform(rotate_z180) * rotation_transform(cut_rotation).inverse();
if (i != instance)
trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo;
rotation = Transformation(trafo).get_rotation();
}
else if (attributes.has(ModelObjectCutAttribute::FlipLower)) {
rotation = rotate_z180;
if (i != instance)
rotation[Z] = rot_z;
}
else if (i != instance)
rotation[Z] = rot_z;
obj_instance->set_rotation(rotation);
}
reset_instance_transformation(lower, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels.size() > 0) {
for (auto dowel : dowels) {
if (!dowel->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
dowel->center_around_origin();
dowel->translate_instances(-dowel->origin_translation);
dowel->origin_translation = Vec3d::Zero();
}
else {
dowel->invalidate_bounding_box();
dowel->center_around_origin();
}
invalidate_translations(dowel, instances[instance]);
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); ++i) {
auto& obj_instance = dowel->instances[i];
const Vec3d offset = obj_instance->get_offset();
Vec3d rotation = Vec3d::Zero();
if (i != instance)
rotation[Z] = obj_instance->get_rotation().z();
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), rotation) * local_dowels_displace;
obj_instance->set_transformation(Geometry::Transformation());
obj_instance->set_offset(offset + displace);
obj_instance->set_rotation(rotation);
}
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
res.push_back(dowel);

View File

@ -271,7 +271,7 @@ struct CutConnectorAttributes
struct CutConnector
{
Vec3d pos;
Vec3d rotation;
Transform3d rotation_m;
float radius;
float height;
float radius_tolerance;// [0.f : 1.f]
@ -279,22 +279,22 @@ struct CutConnector
CutConnectorAttributes attribs;
CutConnector()
: pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f)
: pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f)
{}
CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes)
: pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes)
CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes)
: pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes)
{}
CutConnector(const CutConnector& rhs) :
CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {}
CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {}
bool operator==(const CutConnector& other) const;
bool operator!=(const CutConnector& other) const { return !(other == (*this)); }
template<class Archive> inline void serialize(Archive& ar) {
ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, attribs);
ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs);
}
};
@ -445,8 +445,16 @@ public:
// invalidate cut state for this and related objects from the whole model
void invalidate_cut();
void synchronize_model_after_cut();
ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
void apply_cut_attributes(ModelObjectCutAttributes attributes);
void clone_for_cut(ModelObject **obj);
void process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace);
void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace);
ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs*new_objects);
void merge();
// 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.
@ -760,6 +768,7 @@ public:
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
t_model_material_id material_id() const { return m_material_id; }
void apply_tolerance();
void set_material_id(t_model_material_id material_id);
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);

View File

@ -800,10 +800,9 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar)
{
ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected,
// m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id,
m_ar_plane_center, m_ar_rotations);
m_ar_plane_center, m_rotation_m);
set_center_pos(m_ar_plane_center, true);
m_rotation_m = rotation_transform(m_ar_rotations);
force_update_clipper_on_render = true;
@ -814,7 +813,7 @@ void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const
{
ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected,
// m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id,
m_ar_plane_center, m_ar_rotations);
m_ar_plane_center, m_start_dragging_m);
}
std::string GLGizmoCut3D::on_get_name() const
@ -829,7 +828,7 @@ void GLGizmoCut3D::on_set_state()
// initiate archived values
m_ar_plane_center = m_plane_center;
m_ar_rotations = Transformation(m_rotation_m).get_rotation();
m_start_dragging_m = m_rotation_m;
m_parent.request_extra_frame();
}
@ -1119,8 +1118,6 @@ void GLGizmoCut3D::on_stop_dragging()
m_angle_arc.reset();
m_angle = 0.0;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction);
m_ar_rotations = Transformation(m_rotation_m).get_rotation();
m_start_dragging_m = m_rotation_m;
}
else if (m_hover_id == Z) {
@ -1436,7 +1433,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
void GLGizmoCut3D::render_build_size()
{
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm");
wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm"));
const BoundingBoxf3 tbb = transformed_bounding_box();
Vec3d tbb_sz = tbb.size();
@ -1710,41 +1707,13 @@ bool GLGizmoCut3D::can_perform_cut() const
return tbb.contains(m_plane_center);
}
void GLGizmoCut3D::perform_cut(const Selection& selection)
void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object)
{
const int instance_idx = selection.get_instance_idx();
const int object_idx = selection.get_object_idx();
wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection");
// m_cut_z is the distance from the bed. Subtract possible SLA elevation.
const GLVolume* first_glvolume = selection.get_first_volume();
const double object_cut_z = m_plane_center.z() - first_glvolume->get_sla_shift_z();
const Vec3d& instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset();
Vec3d cut_center_offset = m_plane_center - instance_offset;
cut_center_offset[Z] -= first_glvolume->get_sla_shift_z();
Plater* plater = wxGetApp().plater();
bool create_dowels_as_separate_object = false;
if (0.0 < object_cut_z && can_perform_cut()) {
ModelObject* mo = plater->model().objects[object_idx];
if(!mo)
return;
Vec3d rotation = Transformation(m_rotation_m).get_rotation();
const bool has_connectors = !mo->cut_connectors.empty();
{
Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane"));
// update connectors pos as offset of its center before cut performing
if (has_connectors && m_connector_mode == CutConnectorMode::Manual) {
m_selected.clear();
for (CutConnector& connector : mo->cut_connectors) {
connector.rotation = rotation;
for (CutConnector&connector : mo->cut_connectors) {
connector.rotation_m = m_rotation_m;
if (connector.attribs.type == CutConnectorType::Dowel) {
if (connector.attribs.style == CutConnectorStyle::Prizm)
@ -1761,9 +1730,38 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
}
mo->apply_cut_connectors(_u8L("Connector"));
}
}
void GLGizmoCut3D::perform_cut(const Selection& selection)
{
const int instance_idx = selection.get_instance_idx();
const int object_idx = selection.get_object_idx();
wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection");
Plater* plater = wxGetApp().plater();
ModelObject* mo = plater->model().objects[object_idx];
if (!mo)
return;
// m_cut_z is the distance from the bed. Subtract possible SLA elevation.
const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z();
const double object_cut_z = m_plane_center.z() - sla_shift_z;
const Vec3d instance_offset = mo->instances[instance_idx]->get_offset();
Vec3d cut_center_offset = m_plane_center - instance_offset;
cut_center_offset[Z] -= sla_shift_z;
if (0.0 < object_cut_z && can_perform_cut()) {
bool create_dowels_as_separate_object = false;
const bool has_connectors = !mo->cut_connectors.empty();
{
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane"));
// update connectors pos as offset of its center before cut performing
apply_connectors_in_model(mo, has_connectors, create_dowels_as_separate_object);
}
plater->cut(object_idx, instance_idx, cut_center_offset, rotation,
plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m,
only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) |
@ -1925,7 +1923,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction);
connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(),
connectors.emplace_back(hit, m_rotation_m,
float(m_connector_size) * 0.5f, float(m_connector_depth_ratio),
float(m_connector_size_tolerance) * 0.01f, float(m_connector_depth_ratio_tolerance) * 0.01f,
CutConnectorAttributes( CutConnectorType(m_connector_type),

View File

@ -25,7 +25,6 @@ class GLGizmoCut3D : public GLGizmoBase
// archived values
Vec3d m_ar_plane_center { Vec3d::Zero() };
Vec3d m_ar_rotations { Vec3d::Zero() };
Vec3d m_plane_center{ Vec3d::Zero() };
// data to check position of the cut palne center on gizmo activation
@ -216,6 +215,7 @@ private:
void render_connectors();
bool can_perform_cut() const;
void apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object);
bool cut_line_processing() const;
void discard_cut_line_processing();

View File

@ -1228,6 +1228,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty
case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break;
case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break;
case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break;
case InfoItemType::Cut: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break;
default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break;
}
}

View File

@ -5967,7 +5967,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCut
selection.add_object((unsigned int)(last_id - i), i == 0);
}
void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes)
void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
{
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
auto* object = p->model.objects[obj_idx];
@ -5977,7 +5977,7 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d&
this->suppress_snapshots();
wxBusyCursor wait;
const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes);
const auto new_objects = object->cut(instance_idx, cut_matrix, attributes);
model().delete_object(obj_idx);
sidebar().obj_list()->delete_object_from_list(obj_idx);

View File

@ -256,7 +256,7 @@ public:
void toggle_layers_editing(bool enable);
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes);
void cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes);
void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes);
void export_gcode(bool prefer_removable);
void export_stl_obj(bool extended = false, bool selection_only = false);