NEW:add CutUtils class and its_make_groove_plane for cut tool

Jira:STUDIO-4227
most of code from PrusaSlcer,thanks for PrusaSlcer and YuSanka

commit 1b451cdf9f8859aff58df54fa89d689aa47518d7
Author: YuSanka <yusanka@gmail.com>
Date:   Wed Aug 9 13:22:11 2023 +0200
    CutGizmo: Big code refactoring.
    All manipulations related to cut are extracted to CutUtils now
...

Change-Id: I4e93e19b8e0d46ec2395247796c59587574851cb
This commit is contained in:
Oleksandra Yushchenko 2023-09-18 10:42:53 +08:00 committed by Lane.Wei
parent a92a138918
commit 56ab965af1
8 changed files with 1047 additions and 3 deletions

View File

@ -51,6 +51,8 @@ set(lisbslic3r_sources
Config.hpp
CurveAnalyzer.cpp
CurveAnalyzer.hpp
CutUtils.cpp
CutUtils.hpp
EdgeGrid.cpp
EdgeGrid.hpp
ElephantFootCompensation.cpp

652
src/libslic3r/CutUtils.cpp Normal file
View File

@ -0,0 +1,652 @@
#include "CutUtils.hpp"
#include "Geometry.hpp"
#include "libslic3r.h"
#include "Model.hpp"
#include "TriangleMeshSlicer.hpp"
#include "TriangleSelector.hpp"
#include "ObjectID.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
using namespace Geometry;
static void apply_tolerance(ModelVolume *vol)
{
ModelVolume::CutInfo &cut_info = vol->cut_info;
assert(cut_info.is_connector);
if (!cut_info.is_processed) return;
Vec3d sf = vol->get_scaling_factor();
// make a "hole" wider
sf[X] += double(cut_info.radius_tolerance);
sf[Y] += double(cut_info.radius_tolerance);
// make a "hole" dipper
sf[Z] += double(cut_info.height_tolerance);
vol->set_scaling_factor(sf);
// correct offset in respect to the new depth
Vec3d rot_norm = rotation_transform(vol->get_rotation()) * Vec3d::UnitZ();
if (rot_norm.norm() != 0.0) rot_norm.normalize();
double z_offset = 0.5 * static_cast<double>(cut_info.height_tolerance);
if (cut_info.connector_type == CutConnectorType::Plug || cut_info.connector_type == CutConnectorType::Snap) z_offset -= 0.05; // add small Z offset to better preview
vol->set_offset(vol->get_offset() + rot_norm * z_offset);
}
static void add_cut_volume(TriangleMesh & mesh,
ModelObject * object,
const ModelVolume *src_volume,
const Transform3d &cut_matrix,
const std::string &suffix = {},
ModelVolumeType type = ModelVolumeType::MODEL_PART)
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume *vol = object->add_volume(mesh);
vol->set_type(type);
vol->name = src_volume->name + suffix;
// 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());
vol->cut_info = src_volume->cut_info;
}
static void process_volume_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
ModelObjectCutAttributes attributes,
TriangleMesh & upper_mesh,
TriangleMesh & lower_mesh)
{
const auto volume_matrix = volume->get_matrix();
const Transformation cut_transformation = Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_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(invert_cut_matrix * instance_matrix * volume_matrix, true);
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
static void process_connector_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower,
std::vector<ModelObject *> &dowels)
{
assert(volume->cut_info.is_connector);
volume->cut_info.set_processed();
const auto volume_matrix = volume->get_matrix();
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (volume->cut_info.connector_type != CutConnectorType::Dowel) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume *vol = nullptr;
if (volume->cut_info.connector_type == CutConnectorType::Snap) {
TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.));
vol = upper->add_volume(std::move(mesh));
vol->set_transformation(volume->get_transformation());
vol->set_type(ModelVolumeType::NEGATIVE_VOLUME);
vol->cut_info = volume->cut_info;
vol->name = volume->name;
} else
vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
apply_tolerance(vol);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume *vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
// 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);
}
} else {
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject *dowel{nullptr};
// Clone the object to duplicate instances, materials etc.
volume->get_object()->clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume *vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
dowels.push_back(dowel);
}
// Cut the dowel
apply_tolerance(volume);
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh);
// add small Z offset to better preview
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
// Add cut parts to the related objects
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
}
}
static void process_modifier_cut(ModelVolume *volume, const Transform3d &instance_matrix, const Transform3d &inverse_cut_matrix, ModelObjectCutAttributes attributes, ModelObject *upper, ModelObject *lower)
{
const auto volume_matrix = instance_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(Transformation(volume_matrix));
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
upper->add_volume(*volume);
return;
}
// Some logic for the negative volumes/connectors. Add only needed modifiers
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);
}
static void process_solid_part_cut(
ModelVolume *volume, const Transform3d &instance_matrix, const Transform3d &cut_matrix, ModelObjectCutAttributes attributes, ModelObject *upper, ModelObject *lower)
{
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
if (!lower_mesh.empty()) {
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
upper->volumes.back()->cut_info.is_from_upper = false;
}
return;
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
}
}
static void reset_instance_transformation(ModelObject * object,
size_t src_instance_idx,
const Transform3d &cut_matrix = Transform3d::Identity(),
bool place_on_cut = false,
bool flip = false,
bool is_set_offset = false,
bool offset_pos_dir = true)
{
// 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 double rot_z = obj_instance->get_rotation().z();
Transformation inst_trafo = Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor());
// add respect to mirroring
if (obj_instance->is_left_handed())
inst_trafo = inst_trafo * Transformation(scale_transform(Vec3d(-1, 1, 1)));
obj_instance->set_transformation(inst_trafo);
if (is_set_offset && object->volumes.size() > 0) {
BoundingBoxf3 curBox;
for (size_t i = 0; i < object->volumes.size(); i++) {
curBox.merge(object->volumes[i]->mesh().bounding_box());
}
auto offset_x = curBox.size().x() * 0.7 * (offset_pos_dir ? 1 : -1);
Vec3d displace(offset_x,0,0);
displace = rotation_transform(obj_instance->get_rotation()) * displace;
obj_instance->set_offset(obj_instance->get_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(PI * Vec3d::UnitX());
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);
}
}
Cut::Cut(const ModelObject * object,
int instance,
const Transform3d & cut_matrix,
ModelObjectCutAttributes attributes /*= ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::CutToParts*/)
: m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes)
{
m_model = Model();
if (object) m_model.add_object(*object);
}
void Cut::post_process(ModelObject *object, bool is_upper, ModelObjectPtrs &cut_object_ptrs, bool keep, bool place_on_cut, bool flip)
{
if (!object) return;
if (keep && !object->volumes.empty()) {
reset_instance_transformation(object, m_instance, m_cut_matrix, place_on_cut, flip,set_offset_for_two_part, is_upper);
cut_object_ptrs.push_back(object);
} else
m_model.objects.push_back(object); // will be deleted in m_model.clear_objects();
}
void Cut::post_process(ModelObject *upper, ModelObject *lower, ModelObjectPtrs &cut_object_ptrs)
{
post_process(upper,true, cut_object_ptrs, m_attributes.has(ModelObjectCutAttribute::KeepUpper), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
m_attributes.has(ModelObjectCutAttribute::FlipUpper));
post_process(lower, false, cut_object_ptrs, m_attributes.has(ModelObjectCutAttribute::KeepLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower));
}
void Cut::finalize(const ModelObjectPtrs &objects)
{
// clear model from temporarry objects
m_model.clear_objects();
// add to model result objects
m_model.objects = objects;
}
const ModelObjectPtrs &Cut::perform_with_plane()
{
if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) {
m_model.clear_objects();
return m_model.objects;
}
ModelObject *mo = m_model.objects.front();
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// Clone the object to duplicate instances, materials etc.
ModelObject *upper{nullptr};
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper))
mo->clone_for_cut(&upper);
ModelObject *lower{nullptr};
if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !m_attributes.has(ModelObjectCutAttribute::CutToParts))
mo->clone_for_cut(&lower);
std::vector<ModelObject *> dowels;
// 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 = mo->instances[m_instance]->get_transformation().get_matrix_no_offset();
const Transformation cut_transformation = Transformation(m_cut_matrix);
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset());
for (ModelVolume *volume : mo->volumes) {
volume->reset_extra_facets();
if (!volume->is_model_part()) {
if (volume->cut_info.is_processed){
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, m_attributes, upper, lower);
}
else{
process_connector_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower, dowels);
}
} else if (!volume->mesh().empty()) {
process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower);
}
}
// Post-process cut parts
if (m_attributes.has(ModelObjectCutAttribute::CutToParts) && upper->volumes.empty()) {
m_model = Model();
m_model.objects.push_back(upper);
return m_model.objects;
}
ModelObjectPtrs cut_object_ptrs;
if (m_attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) {
reset_instance_transformation(upper, m_instance, m_cut_matrix);
cut_object_ptrs.push_back(upper);
} else {
// Delete all modifiers which are not intersecting with solid parts bounding box
auto delete_extra_modifiers = [this](ModelObject *mo) {
if (!mo) return;
const BoundingBoxf3 obj_bb = mo->instance_bounding_box(m_instance);
const Transform3d inst_matrix = mo->instances[m_instance]->get_transformation().get_matrix();
for (int i = int(mo->volumes.size()) - 1; i >= 0; --i)
if (const ModelVolume *vol = mo->volumes[i]; !vol->is_model_part() && !vol->is_cut_connector()) {
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
if (!obj_bb.intersects(bb))
mo->delete_volume(i);
}
};
post_process(upper, lower, cut_object_ptrs);
delete_extra_modifiers(upper);
delete_extra_modifiers(lower);
if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
reset_instance_transformation(dowel, m_instance);
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
cut_object_ptrs.push_back(dowel);
}
}
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
finalize(cut_object_ptrs);
return m_model.objects;
}
static void distribute_modifiers_from_object(ModelObject *from_obj, const int instance_idx, ModelObject *to_obj1, ModelObject *to_obj2)
{
auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3();
auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3();
const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix();
for (ModelVolume *vol : from_obj->volumes)
if (!vol->is_model_part()) {
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
// Don't add modifiers which are not intersecting with solid parts
if (obj1_bb.intersects(bb)) to_obj1->add_volume(*vol);
if (obj2_bb.intersects(bb)) to_obj2->add_volume(*vol);
}
}
static void merge_solid_parts_inside_object(ModelObjectPtrs &objects)
{
for (ModelObject *mo : objects) {
TriangleMesh mesh;
// Merge all SolidPart but not Connectors
for (const ModelVolume *mv : mo->volumes) {
if (mv->is_model_part() && !mv->is_cut_connector()) {
TriangleMesh m = mv->mesh();
m.transform(mv->get_matrix());
mesh.merge(m);
}
}
if (!mesh.empty()) {
ModelVolume *new_volume = mo->add_volume(mesh);
new_volume->name = mo->name;
// Delete all merged SolidPart but not Connectors
for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) {
const ModelVolume *mv = mo->volumes[i];
if (mv->is_model_part() && !mv->is_cut_connector()) mo->delete_volume(i);
}
}
}
}
const ModelObjectPtrs &Cut::perform_by_contour(std::vector<Part> parts, int dowels_count)
{
ModelObject *cut_mo = m_model.objects.front();
// Clone the object to duplicate instances, materials etc.
ModelObject *upper{nullptr};
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper))
cut_mo->clone_for_cut(&upper);
ModelObject *lower{nullptr};
if (m_attributes.has(ModelObjectCutAttribute::KeepLower))
cut_mo->clone_for_cut(&lower);
const size_t cut_parts_cnt = parts.size();
bool has_modifiers = false;
// Distribute SolidParts to the Upper/Lower object
for (size_t id = 0; id < cut_parts_cnt; ++id) {
if (parts[id].is_modifier)
has_modifiers = true; // modifiers will be added later to the related parts
else if (ModelObject *obj = (parts[id].selected ? upper : lower))
obj->add_volume(*(cut_mo->volumes[id]));
}
if (has_modifiers) {
// Distribute Modifiers to the Upper/Lower object
distribute_modifiers_from_object(cut_mo, m_instance, upper, lower);
}
ModelObjectPtrs cut_object_ptrs;
ModelVolumePtrs &volumes = cut_mo->volumes;
if (volumes.size() == cut_parts_cnt) {
// Means that object is cut without connectors
// Just add Upper and Lower objects to cut_object_ptrs
post_process(upper, lower, cut_object_ptrs);
} else if (volumes.size() > cut_parts_cnt) {
// Means that object is cut with connectors
// All volumes are distributed to Upper / Lower object,
// So we dont need them anymore
for (size_t id = 0; id < cut_parts_cnt; id++) delete *(volumes.begin() + id);
volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt);
// Perform cut just to get connectors
Cut cut(cut_mo, m_instance, m_cut_matrix, m_attributes);
const ModelObjectPtrs &cut_connectors_obj = cut.perform_with_plane();
assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2);
// Connectors from upper object
for (const ModelVolume *volume : cut_connectors_obj[0]->volumes) upper->add_volume(*volume, volume->type());
// Connectors from lower object
for (const ModelVolume *volume : cut_connectors_obj[1]->volumes) lower->add_volume(*volume, volume->type());
// Add Upper and Lower objects to cut_object_ptrs
post_process(upper, lower, cut_object_ptrs);
// Add Dowel-connectors as separate objects to cut_object_ptrs
if (cut_connectors_obj.size() >= 3)
for (size_t id = 2; id < cut_connectors_obj.size(); id++) cut_object_ptrs.push_back(cut_connectors_obj[id]);
}
// Now merge all model parts together:
merge_solid_parts_inside_object(cut_object_ptrs);
finalize(cut_object_ptrs);
return m_model.objects;
}
const ModelObjectPtrs &Cut::perform_with_groove(const Groove &groove, const Transform3d &rotation_m, bool keep_as_parts /* = false*/)
{
ModelObject *cut_mo = m_model.objects.front();
// Clone the object to duplicate instances, materials etc.
ModelObject *upper{nullptr};
cut_mo->clone_for_cut(&upper);
ModelObject *lower{nullptr};
cut_mo->clone_for_cut(&lower);
const double groove_half_depth = 0.5 * double(groove.depth);
Model tmp_model_for_cut = Model();
Model tmp_model = Model();
tmp_model.add_object(*cut_mo);
ModelObject *tmp_object = tmp_model.objects.front();
auto add_volumes_from_cut = [](ModelObject *object, const ModelObjectCutAttribute attribute, const Model &tmp_model_for_cut) {
const auto &volumes = tmp_model_for_cut.objects.front()->volumes;
for (const ModelVolume *volume : volumes)
if (volume->is_model_part()) {
if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) ||
(attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper())) {
ModelVolume *new_vol = object->add_volume(*volume);
new_vol->reset_from_upper();
}
}
};
auto cut = [this, add_volumes_from_cut](ModelObject *object, const Transform3d &cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model &tmp_model_for_cut) {
Cut cut(object, m_instance, cut_matrix);
tmp_model_for_cut = Model();
tmp_model_for_cut.add_object(*cut.perform_with_plane().front());
assert(!tmp_model_for_cut.objects.empty());
object->clear_volumes();
add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut);
reset_instance_transformation(object, m_instance);
};
// cut by upper plane
const Transform3d cut_matrix_upper = translation_transform(rotation_m * (groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix;
{
cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// cut by lower plane
const Transform3d cut_matrix_lower = translation_transform(rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix;
{
cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
}
// cut middle part with 2 angles and add parts to related upper/lower objects
const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle));
// cut by angle1 plane
{
const Transform3d cut_matrix_angle1 = translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * m_cut_matrix *
rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle));
cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// cut by angle2 plane
{
const Transform3d cut_matrix_angle2 = translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * m_cut_matrix *
rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle));
cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// apply tolerance to the middle part
{
const double h_groove_shift_tolerance = groove_half_depth - (double) groove.depth_tolerance;
const Transform3d cut_matrix_lower_tolerance = translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * m_cut_matrix;
cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
const double h_side_shift_tolerance = h_side_shift - 0.5 * double(groove.width_tolerance);
const Transform3d cut_matrix_angle1_tolerance = translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix *
rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle));
cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
const Transform3d cut_matrix_angle2_tolerance = translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix *
rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle));
cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// this part can be added to the upper object now
add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
ModelObjectPtrs cut_object_ptrs;
if (keep_as_parts) {
// add volumes from lower object to the upper, but mark them as a lower
const auto &volumes = lower->volumes;
for (const ModelVolume *volume : volumes) {
ModelVolume *new_vol = upper->add_volume(*volume);
new_vol->cut_info.is_from_upper = false;
}
// add modifiers
for (const ModelVolume *volume : cut_mo->volumes)
if (!volume->is_model_part()) upper->add_volume(*volume);
cut_object_ptrs.push_back(upper);
// add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks
cut_object_ptrs.push_back(lower);
} else {
// add modifiers if object has any
for (const ModelVolume *volume : cut_mo->volumes)
if (!volume->is_model_part()) {
distribute_modifiers_from_object(cut_mo, m_instance, upper, lower);
break;
}
assert(!upper->volumes.empty() && !lower->volumes.empty());
// Add Upper and Lower parts to cut_object_ptrs
post_process(upper, lower, cut_object_ptrs);
// Now merge all model parts together:
merge_solid_parts_inside_object(cut_object_ptrs);
}
finalize(cut_object_ptrs);
return m_model.objects;
}
} // namespace Slic3r

View File

@ -0,0 +1,58 @@
#ifndef slic3r_CutUtils_hpp_
#define slic3r_CutUtils_hpp_
#include "enum_bitmask.hpp"
#include "Point.hpp"
#include "Model.hpp"
namespace Slic3r {
struct Groove
{
float depth{0.f};
float width{0.f};
float flaps_angle{0.f};
float angle{0.f};
float depth_init{0.f};
float width_init{0.f};
float flaps_angle_init{0.f};
float angle_init{0.f};
float depth_tolerance{0.1f};
float width_tolerance{0.1f};
};
class Cut {
Model m_model;
int m_instance;
const Transform3d m_cut_matrix;
ModelObjectCutAttributes m_attributes;
void post_process(ModelObject *object, bool is_upper, ModelObjectPtrs &objects, bool keep, bool place_on_cut, bool flip);
void post_process(ModelObject* upper_object, ModelObject* lower_object, ModelObjectPtrs& objects);
void finalize(const ModelObjectPtrs& objects);
public:
Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper |
ModelObjectCutAttribute::KeepLower |
ModelObjectCutAttribute::CutToParts );
~Cut() { m_model.clear_objects(); }
struct Part
{
bool selected;
bool is_modifier;
};
const ModelObjectPtrs& perform_with_plane();
const ModelObjectPtrs& perform_by_contour(std::vector<Part> parts, int dowels_count);
const ModelObjectPtrs& perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts = false);
bool set_offset_for_two_part{false};
}; // namespace Cut
} // namespace Slic3r
#endif /* slic3r_CutUtils_hpp_ */

View File

@ -2783,6 +2783,12 @@ void ModelVolume::set_material_id(t_model_material_id material_id)
this->object->get_model()->add_material(material_id);
}
void ModelVolume::reset_extra_facets() {
this->supported_facets.reset();
this->seam_facets.reset();
this->mmu_segmentation_facets.reset();
}
ModelMaterial* ModelVolume::material() const
{
return this->object->get_model()->get_material(m_material_id);

View File

@ -848,6 +848,7 @@ public:
// It contains information about connetors
struct CutInfo
{
bool is_from_upper{true};
bool is_connector{false};
bool is_processed{true};
CutConnectorType connector_type{CutConnectorType::Plug};
@ -864,11 +865,14 @@ public:
void set_processed() { is_processed = true; }
void invalidate() { is_connector = false; }
void reset_from_upper() { is_from_upper = true; }
template<class Archive> inline void serialize(Archive &ar) { ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); }
};
CutInfo cut_info;
bool is_from_upper() const { return cut_info.is_from_upper; }
void reset_from_upper() { cut_info.reset_from_upper(); }
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
void invalidate_cut_info() { cut_info.invalidate(); }
@ -914,6 +918,7 @@ public:
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 set_material_id(t_model_material_id material_id);
void reset_extra_facets();
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
// Extract the current extruder ID based on this ModelVolume's config and the parent ModelObject's config.

View File

@ -8,6 +8,7 @@
#include "Point.hpp"
#include "Execution/ExecutionTBB.hpp"
#include "Execution/ExecutionSeq.hpp"
#include "CutUtils.hpp"
#include "Utils.hpp"
#include "Format/STL.hpp"
#include <libqhullcpp/Qhull.h>
@ -1234,6 +1235,325 @@ indexed_triangle_set its_make_snap(double r, double h, float space_proportion, f
return mesh;
}
indexed_triangle_set its_make_groove_plane(const Groove &cur_groove, float rotate_radius, std::vector<Vec3d> &cur_groove_vertices) {
// values for calculation
const float side_width = is_approx(cur_groove.flaps_angle, 0.f) ? cur_groove.depth : (cur_groove.depth / sin(cur_groove.flaps_angle));
const float flaps_width = 2.f * side_width * cos(cur_groove.flaps_angle);
const float groove_half_width_upper = 0.5f * (cur_groove.width);
const float groove_half_width_lower = 0.5f * (cur_groove.width + flaps_width);
const float cut_plane_radius = 1.5f * float(rotate_radius);
const float cut_plane_length = 1.5f * cut_plane_radius;
const float groove_half_depth = 0.5f * cur_groove.depth;
const float x = 0.5f * cut_plane_radius;
const float y = 0.5f * cut_plane_length;
float z_upper = groove_half_depth;
float z_lower = -groove_half_depth;
const float proj = y * tan(cur_groove.angle);
float ext_upper_x = groove_half_width_upper + proj; // upper_x extension
float ext_lower_x = groove_half_width_lower + proj; // lower_x extension
float nar_upper_x = groove_half_width_upper - proj; // upper_x narrowing
float nar_lower_x = groove_half_width_lower - proj; // lower_x narrowing
const float cut_plane_thiknes = 0.02f; // 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut_plane_thiknes
// Vertices of the groove used to detection if groove is valid
// They are written as:
// {left_ext_lower, left_nar_lower, left_ext_upper, left_nar_upper,
// right_ext_lower, right_nar_lower, right_ext_upper, right_nar_upper }
{
cur_groove_vertices.clear();
cur_groove_vertices.reserve(8);
cur_groove_vertices.emplace_back(Vec3f(-ext_lower_x, -y, z_lower).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(-nar_lower_x, y, z_lower).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(-ext_upper_x, -y, z_upper).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(-nar_upper_x, y, z_upper).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(ext_lower_x, -y, z_lower).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(nar_lower_x, y, z_lower).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(ext_upper_x, -y, z_upper).cast<double>());
cur_groove_vertices.emplace_back(Vec3f(nar_upper_x, y, z_upper).cast<double>());
}
// Different cases of groove plane:
// groove is open
if (groove_half_width_upper > proj && groove_half_width_lower > proj) {
indexed_triangle_set mesh;
auto get_vertices = [x, y](float z_upper, float z_lower, float nar_upper_x, float nar_lower_x, float ext_upper_x, float ext_lower_x) {
return std::vector<stl_vertex>({// upper left part vertices
{-x, -y, z_upper},
{-x, y, z_upper},
{-nar_upper_x, y, z_upper},
{-ext_upper_x, -y, z_upper},
// lower part vertices
{-ext_lower_x, -y, z_lower},
{-nar_lower_x, y, z_lower},
{nar_lower_x, y, z_lower},
{ext_lower_x, -y, z_lower},
// upper right part vertices
{ext_upper_x, -y, z_upper},
{nar_upper_x, y, z_upper},
{x, y, z_upper},
{x, -y, z_upper}});
};
mesh.vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x);
mesh.vertices.reserve(2 * mesh.vertices.size());
z_upper -= cut_plane_thiknes;
z_lower -= cut_plane_thiknes;
const float under_x_shift = cut_plane_thiknes / tan(0.5f * cur_groove.flaps_angle);
nar_upper_x += under_x_shift;
nar_lower_x += under_x_shift;
ext_upper_x += under_x_shift;
ext_lower_x += under_x_shift;
std::vector<stl_vertex> vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x);
mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end());
mesh.indices = {// above view
{5, 4, 7},
{5, 7, 6}, // lower part
{3, 4, 5},
{3, 5, 2}, // left side
{9, 6, 8},
{8, 6, 7}, // right side
{1, 0, 2},
{2, 0, 3}, // upper left part
{9, 8, 10},
{10, 8, 11}, // upper right part
// under view
{20, 21, 22},
{20, 22, 23}, // upper right part
{12, 13, 14},
{12, 14, 15}, // upper left part
{18, 21, 20},
{18, 20, 19}, // right side
{16, 15, 14},
{16, 14, 17}, // left side
{16, 17, 18},
{16, 18, 19}, // lower part
// left edge
{1, 13, 0},
{0, 13, 12},
// front edge
{0, 12, 3},
{3, 12, 15},
{3, 15, 4},
{4, 15, 16},
{4, 16, 7},
{7, 16, 19},
{7, 19, 20},
{7, 20, 8},
{8, 20, 11},
{11, 20, 23},
// right edge
{11, 23, 10},
{10, 23, 22},
// back edge
{1, 13, 2},
{2, 13, 14},
{2, 14, 17},
{2, 17, 5},
{5, 17, 6},
{6, 17, 18},
{6, 18, 9},
{9, 18, 21},
{9, 21, 10},
{10, 21, 22}};
return mesh;
}
float cross_pt_upper_y = groove_half_width_upper / tan(cur_groove.angle);
// groove is closed
if (groove_half_width_upper < proj && groove_half_width_lower < proj) {
float cross_pt_lower_y = groove_half_width_lower / tan(cur_groove.angle);
indexed_triangle_set mesh;
auto get_vertices = [x, y](float z_upper, float z_lower, float cross_pt_upper_y, float cross_pt_lower_y, float ext_upper_x, float ext_lower_x) {
return std::vector<stl_vertex>({// upper part vertices
{-x, -y, z_upper},
{-x, y, z_upper},
{x, y, z_upper},
{x, -y, z_upper},
{ext_upper_x, -y, z_upper},
{0.f, cross_pt_upper_y, z_upper},
{-ext_upper_x, -y, z_upper},
// lower part vertices
{-ext_lower_x, -y, z_lower},
{0.f, cross_pt_lower_y, z_lower},
{ext_lower_x, -y, z_lower}});
};
mesh.vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x);
mesh.vertices.reserve(2 * mesh.vertices.size());
z_upper -= cut_plane_thiknes;
z_lower -= cut_plane_thiknes;
const float under_x_shift = cut_plane_thiknes / tan(0.5f * cur_groove.flaps_angle);
cross_pt_upper_y += cut_plane_thiknes;
cross_pt_lower_y += cut_plane_thiknes;
ext_upper_x += under_x_shift;
ext_lower_x += under_x_shift;
std::vector<stl_vertex> vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x);
mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end());
mesh.indices = { // above view
{8, 7, 9}, // lower part
{5, 8, 6},
{6, 8, 7}, // left side
{4, 9, 8},
{4, 8, 5}, // right side
{1, 0, 6},
{1, 6, 5},
{1, 5, 2},
{2, 5, 4},
{2, 4, 3}, // upper part
// under view
{10, 11, 16},
{16, 11, 15},
{15, 11, 12},
{15, 12, 14},
{14, 12, 13}, // upper part
{18, 15, 14},
{14, 18, 19}, // right side
{17, 16, 15},
{17, 15, 18}, // left side
{17, 18, 19}, // lower part
// left edge
{1, 11, 0},
{0, 11, 10},
// front edge
{0, 10, 6},
{6, 10, 16},
{6, 17, 16},
{6, 7, 17},
{7, 17, 19},
{7, 19, 9},
{4, 14, 19},
{4, 19, 9},
{4, 14, 13},
{4, 13, 3},
// right edge
{3, 13, 12},
{3, 12, 2},
// back edge
{2, 12, 11},
{2, 11, 1}};
return mesh;
}
// groove is closed from the roof
indexed_triangle_set mesh;
mesh.vertices = {// upper part vertices
{-x, -y, z_upper},
{-x, y, z_upper},
{x, y, z_upper},
{x, -y, z_upper},
{ext_upper_x, -y, z_upper},
{0.f, cross_pt_upper_y, z_upper},
{-ext_upper_x, -y, z_upper},
// lower part vertices
{-ext_lower_x, -y, z_lower},
{-nar_lower_x, y, z_lower},
{nar_lower_x, y, z_lower},
{ext_lower_x, -y, z_lower}};
mesh.vertices.reserve(2 * mesh.vertices.size() + 1);
z_upper -= cut_plane_thiknes;
z_lower -= cut_plane_thiknes;
const float under_x_shift = cut_plane_thiknes / tan(0.5f * cur_groove.flaps_angle);
nar_lower_x += under_x_shift;
ext_upper_x += under_x_shift;
ext_lower_x += under_x_shift;
std::vector<stl_vertex> vertices = {// upper part vertices
{-x, -y, z_upper},
{-x, y, z_upper},
{x, y, z_upper},
{x, -y, z_upper},
{ext_upper_x, -y, z_upper},
{under_x_shift, cross_pt_upper_y, z_upper},
{-under_x_shift, cross_pt_upper_y, z_upper},
{-ext_upper_x, -y, z_upper},
// lower part vertices
{-ext_lower_x, -y, z_lower},
{-nar_lower_x, y, z_lower},
{nar_lower_x, y, z_lower},
{ext_lower_x, -y, z_lower}};
mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end());
mesh.indices = {// above view
{8, 7, 10},
{8, 10, 9}, // lower part
{5, 8, 7},
{5, 7, 6}, // left side
{4, 10, 9},
{4, 9, 5}, // right side
{1, 0, 6},
{1, 6, 5},
{1, 5, 2},
{2, 5, 4},
{2, 4, 3}, // upper part
// under view
{11, 12, 18},
{18, 12, 17},
{17, 12, 16},
{16, 12, 13},
{16, 13, 15},
{15, 13, 14}, // upper part
{21, 16, 15},
{21, 15, 22}, // right side
{19, 18, 17},
{19, 17, 20}, // left side
{19, 20, 21},
{19, 21, 22}, // lower part
// left edge
{1, 12, 11},
{1, 11, 0},
// front edge
{0, 11, 18},
{0, 18, 6},
{7, 19, 18},
{7, 18, 6},
{7, 19, 22},
{7, 22, 10},
{10, 22, 15},
{10, 15, 4},
{4, 15, 14},
{4, 14, 3},
// right edge
{3, 14, 13},
{3, 14, 2},
// back edge
{2, 13, 12},
{2, 12, 1},
{5, 16, 21},
{5, 21, 9},
{9, 21, 20},
{9, 20, 8},
{5, 17, 20},
{5, 20, 8}};
return mesh;
}
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
{
std::vector<Vec3f> dst_vertices;

View File

@ -15,7 +15,7 @@ namespace Slic3r {
class TriangleMesh;
class TriangleMeshSlicer;
struct Groove;
struct RepairedMeshErrors {
// How many edges were united by merging their end points with some other end points in epsilon neighborhood?
int edges_fixed = 0;
@ -341,6 +341,7 @@ indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCou
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);
indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f);
indexed_triangle_set its_make_groove_plane(const Groove &cur_groove, float rotate_radius, std::vector<Vec3d> &cur_groove_vertices);
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }

View File

@ -318,7 +318,7 @@ public:
const IndicesList& get_volume_idxs() const { return m_list; }
const GLVolume* get_volume(unsigned int volume_idx) const;
const GLVolume* get_first_volume() const { return get_volume(*m_list.begin()); }
const ObjectIdxsToInstanceIdxsMap& get_content() const { return m_cache.content; }
unsigned int volumes_count() const { return (unsigned int)m_list.size(); }