mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 19:39:05 +08:00
CutGizmo: Big code refactoring.
All manipulations related to cut are extracted to CutUtils now
This commit is contained in:
parent
190a4cde48
commit
1b451cdf9f
@ -40,6 +40,7 @@
|
|||||||
#include "libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/CutUtils.hpp"
|
||||||
#include "libslic3r/ModelArrange.hpp"
|
#include "libslic3r/ModelArrange.hpp"
|
||||||
#include "libslic3r/Platform.hpp"
|
#include "libslic3r/Platform.hpp"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
@ -437,8 +438,11 @@ int CLI::run(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
|
// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
|
||||||
model.objects.front()->cut(0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
|
Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
|
||||||
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
|
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
|
||||||
|
auto cut_objects = cut.perform_with_plane();
|
||||||
|
for (ModelObject* obj : cut_objects)
|
||||||
|
model.add_object(*obj);
|
||||||
#endif
|
#endif
|
||||||
model.delete_object(size_t(0));
|
model.delete_object(size_t(0));
|
||||||
}
|
}
|
||||||
|
@ -201,6 +201,8 @@ set(SLIC3R_SOURCES
|
|||||||
BlacklistedLibraryCheck.hpp
|
BlacklistedLibraryCheck.hpp
|
||||||
LocalesUtils.cpp
|
LocalesUtils.cpp
|
||||||
LocalesUtils.hpp
|
LocalesUtils.hpp
|
||||||
|
CutUtils.cpp
|
||||||
|
CutUtils.hpp
|
||||||
Model.cpp
|
Model.cpp
|
||||||
Model.hpp
|
Model.hpp
|
||||||
ModelArrange.hpp
|
ModelArrange.hpp
|
||||||
|
627
src/libslic3r/CutUtils.cpp
Normal file
627
src/libslic3r/CutUtils.cpp
Normal file
@ -0,0 +1,627 @@
|
|||||||
|
|
||||||
|
#include "CutUtils.hpp"
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "Model.hpp"
|
||||||
|
#include "TriangleMeshSlicer.hpp"
|
||||||
|
#include "TriangleSelector.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::KeepAsParts)) {
|
||||||
|
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::KeepAsParts)) {
|
||||||
|
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor()));
|
||||||
|
|
||||||
|
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::KeepAsParts*/)
|
||||||
|
: m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes)
|
||||||
|
{
|
||||||
|
m_model = Model();
|
||||||
|
if (object)
|
||||||
|
m_model.add_object(*object);
|
||||||
|
}
|
||||||
|
|
||||||
|
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::KeepAsParts))
|
||||||
|
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::KeepAsParts) && 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::KeepAsParts) && !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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
|
||||||
|
delete_extra_modifiers(upper);
|
||||||
|
reset_instance_transformation(upper, m_instance, m_cut_matrix,
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::FlipUpper));
|
||||||
|
cut_object_ptrs.push_back(upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
|
||||||
|
delete_extra_modifiers(lower);
|
||||||
|
reset_instance_transformation(lower, m_instance, m_cut_matrix,
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower));
|
||||||
|
cut_object_ptrs.push_back(lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
||||||
|
for (auto dowel : dowels) {
|
||||||
|
reset_instance_transformation(dowel, m_instance, Transform3d::Identity());
|
||||||
|
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
||||||
|
cut_object_ptrs.push_back(dowel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
||||||
|
|
||||||
|
m_model.clear_objects();
|
||||||
|
m_model.objects = 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);
|
||||||
|
|
||||||
|
auto add_cut_objects = [this](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) {
|
||||||
|
if (upper && !upper->volumes.empty()) {
|
||||||
|
reset_instance_transformation(upper, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper));
|
||||||
|
cut_objects.push_back(upper);
|
||||||
|
}
|
||||||
|
if (lower && !lower->volumes.empty()) {
|
||||||
|
reset_instance_transformation(lower, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower));
|
||||||
|
cut_objects.push_back(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
|
||||||
|
add_cut_objects(cut_object_ptrs, upper, lower);
|
||||||
|
}
|
||||||
|
else if (volumes.size() > cut_parts_cnt) {
|
||||||
|
// Means that object is cut with connectors
|
||||||
|
|
||||||
|
// All volumes are distributed to Upper / Lower object,
|
||||||
|
// So we don’t 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
|
||||||
|
add_cut_objects(cut_object_ptrs, upper, lower);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
m_model.clear_objects();
|
||||||
|
m_model.objects = 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());
|
||||||
|
|
||||||
|
reset_instance_transformation(upper, m_instance, cut_matrix_upper, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper));
|
||||||
|
cut_object_ptrs.push_back(upper);
|
||||||
|
reset_instance_transformation(lower, m_instance, cut_matrix_lower, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::FlipUpper));
|
||||||
|
cut_object_ptrs.push_back(lower);
|
||||||
|
|
||||||
|
// Now merge all model parts together:
|
||||||
|
merge_solid_parts_inside_object(cut_object_ptrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_model.clear_objects();
|
||||||
|
m_model.objects = cut_object_ptrs;
|
||||||
|
|
||||||
|
return m_model.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
65
src/libslic3r/CutUtils.hpp
Normal file
65
src/libslic3r/CutUtils.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef slic3r_CutUtils_hpp_
|
||||||
|
#define slic3r_CutUtils_hpp_
|
||||||
|
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "enum_bitmask.hpp"
|
||||||
|
#include "Geometry.hpp"
|
||||||
|
#include "ObjectID.hpp"
|
||||||
|
#include "Point.hpp"
|
||||||
|
#include "Model.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
using ModelObjectPtrs = std::vector<ModelObject*>;
|
||||||
|
|
||||||
|
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo };
|
||||||
|
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
||||||
|
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
||||||
|
|
||||||
|
|
||||||
|
class Cut {
|
||||||
|
|
||||||
|
Model m_model;
|
||||||
|
int m_instance;
|
||||||
|
const Transform3d& m_cut_matrix;
|
||||||
|
ModelObjectCutAttributes m_attributes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
|
||||||
|
ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper |
|
||||||
|
ModelObjectCutAttribute::KeepLower |
|
||||||
|
ModelObjectCutAttribute::KeepAsParts );
|
||||||
|
~Cut() { m_model.clear_objects(); }
|
||||||
|
|
||||||
|
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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}; // namespace Cut
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* slic3r_CutUtils_hpp_ */
|
@ -1332,327 +1332,6 @@ void ModelVolume::reset_extra_facets()
|
|||||||
this->mmu_segmentation_facets.reset();
|
this->mmu_segmentation_facets.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelVolume::apply_tolerance()
|
|
||||||
{
|
|
||||||
assert(cut_info.is_connector);
|
|
||||||
if (!cut_info.is_processed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vec3d sf = 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);
|
|
||||||
|
|
||||||
set_scaling_factor(sf);
|
|
||||||
|
|
||||||
// correct offset in respect to the new depth
|
|
||||||
Vec3d rot_norm = Geometry::rotation_transform(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
|
|
||||||
|
|
||||||
set_offset(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelObject::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);
|
|
||||||
vol->apply_tolerance();
|
|
||||||
}
|
|
||||||
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.
|
|
||||||
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
|
|
||||||
volume->apply_tolerance();
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = 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(Geometry::Transformation(volume_matrix));
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelObject::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();
|
|
||||||
|
|
||||||
using namespace Geometry;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
void ModelObject::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::KeepAsParts)) {
|
|
||||||
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
|
|
||||||
bool place_on_cut/* = false*/, bool flip/* = false*/)
|
|
||||||
{
|
|
||||||
using namespace Geometry;
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor()));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
// 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) && !attributes.has(ModelObjectCutAttribute::KeepAsParts))
|
|
||||||
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 = instances[instance]->get_transformation().get_matrix_no_offset();
|
|
||||||
const Transformation cut_transformation = Transformation(cut_matrix);
|
|
||||||
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset());
|
|
||||||
|
|
||||||
for (ModelVolume* volume : 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, attributes, upper, lower);
|
|
||||||
else
|
|
||||||
process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels);
|
|
||||||
}
|
|
||||||
else if (!volume->mesh().empty())
|
|
||||||
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-process cut parts
|
|
||||||
|
|
||||||
ModelObjectPtrs res;
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty())
|
|
||||||
return res;
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) {
|
|
||||||
reset_instance_transformation(upper, instance, cut_matrix);
|
|
||||||
res.push_back(upper);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Delete all modifiers which are not intersecting with solid parts bounding box
|
|
||||||
auto delete_extra_modifiers = [instance](ModelObject* mo) {
|
|
||||||
if (!mo) return;
|
|
||||||
const BoundingBoxf3 obj_bb = mo->instance_bounding_box(instance);
|
|
||||||
const Transform3d inst_matrix = mo->instances[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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
|
|
||||||
delete_extra_modifiers(upper);
|
|
||||||
reset_instance_transformation(upper, instance, cut_matrix,
|
|
||||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
|
||||||
attributes.has(ModelObjectCutAttribute::FlipUpper));
|
|
||||||
res.push_back(upper);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
|
|
||||||
delete_extra_modifiers(lower);
|
|
||||||
reset_instance_transformation(lower, instance, cut_matrix,
|
|
||||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
|
||||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower));
|
|
||||||
res.push_back(lower);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
|
||||||
for (auto dowel : dowels) {
|
|
||||||
reset_instance_transformation(dowel, instance, Transform3d::Identity());
|
|
||||||
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
|
||||||
res.push_back(dowel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare TriangleMeshes by Bounding boxes (mainly for sort)
|
/// Compare TriangleMeshes by Bounding boxes (mainly for sort)
|
||||||
|
@ -317,10 +317,6 @@ enum class ModelVolumeType : int {
|
|||||||
SUPPORT_ENFORCER,
|
SUPPORT_ENFORCER,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo };
|
|
||||||
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
|
||||||
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
|
||||||
|
|
||||||
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
|
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
|
||||||
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
|
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
|
||||||
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
|
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
|
||||||
@ -468,21 +464,6 @@ public:
|
|||||||
void delete_connectors();
|
void delete_connectors();
|
||||||
void clone_for_cut(ModelObject **obj);
|
void clone_for_cut(ModelObject **obj);
|
||||||
|
|
||||||
private:
|
|
||||||
void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
|
|
||||||
std::vector<ModelObject*>& dowels);
|
|
||||||
void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
|
|
||||||
void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh);
|
|
||||||
void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
|
|
||||||
public:
|
|
||||||
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);
|
|
||||||
|
|
||||||
ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes);
|
|
||||||
void split(ModelObjectPtrs*new_objects);
|
void split(ModelObjectPtrs*new_objects);
|
||||||
void merge();
|
void merge();
|
||||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||||
@ -850,7 +831,6 @@ public:
|
|||||||
bool is_the_only_one_part() const; // behave like an object
|
bool is_the_only_one_part() const; // behave like an object
|
||||||
t_model_material_id material_id() const { return m_material_id; }
|
t_model_material_id material_id() const { return m_material_id; }
|
||||||
void reset_extra_facets();
|
void reset_extra_facets();
|
||||||
void apply_tolerance();
|
|
||||||
void set_material_id(t_model_material_id material_id);
|
void set_material_id(t_model_material_id material_id);
|
||||||
ModelMaterial* material() const;
|
ModelMaterial* material() const;
|
||||||
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
||||||
|
@ -675,7 +675,7 @@ static double get_grabber_mean_size(const BoundingBoxf3& bb)
|
|||||||
|
|
||||||
indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane()
|
indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane()
|
||||||
{
|
{
|
||||||
const float ghw = 0.5f * m_groove_width; // groove half width
|
const float ghw = 0.5f * m_groove.width; // groove half width
|
||||||
const float cpr = 1.5f * float(m_radius); // cut plane radius
|
const float cpr = 1.5f * float(m_radius); // cut plane radius
|
||||||
|
|
||||||
const float cpl = 1.5f * cpr; // cut plane length
|
const float cpl = 1.5f * cpr; // cut plane length
|
||||||
@ -684,7 +684,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane()
|
|||||||
// We need two upward facing triangles
|
// We need two upward facing triangles
|
||||||
float x = 0.5f * cpr, y = 0.5f * cpl;
|
float x = 0.5f * cpr, y = 0.5f * cpl;
|
||||||
|
|
||||||
const float proj = y * tan(m_groove_angle);
|
const float proj = y * tan(m_groove.angle);
|
||||||
const float extension_x = ghw + proj;
|
const float extension_x = ghw + proj;
|
||||||
|
|
||||||
// upper cut plane is simple
|
// upper cut plane is simple
|
||||||
@ -712,7 +712,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane()
|
|||||||
|
|
||||||
if (ghw < proj) {
|
if (ghw < proj) {
|
||||||
|
|
||||||
const float cross_pt_y = ghw / tan(m_groove_angle);
|
const float cross_pt_y = ghw / tan(m_groove.angle);
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
// roof
|
// roof
|
||||||
@ -756,7 +756,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane()
|
|||||||
|
|
||||||
indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width)
|
indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width)
|
||||||
{
|
{
|
||||||
const float ghw = 0.5f * (m_groove_width + flaps_width); // groove half width
|
const float ghw = 0.5f * (m_groove.width + flaps_width); // groove half width
|
||||||
const float cpr = 1.5f * float(m_radius); // cut plane radius
|
const float cpr = 1.5f * float(m_radius); // cut plane radius
|
||||||
|
|
||||||
const float cpl = 1.5f * cpr; // cut plane length
|
const float cpl = 1.5f * cpr; // cut plane length
|
||||||
@ -765,7 +765,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width
|
|||||||
// We need two upward facing triangles
|
// We need two upward facing triangles
|
||||||
float x = 0.5f * cpr, y = 0.5f * cpl;
|
float x = 0.5f * cpr, y = 0.5f * cpl;
|
||||||
|
|
||||||
const float proj = y * tan(m_groove_angle);
|
const float proj = y * tan(m_groove.angle);
|
||||||
const float extension_x = ghw + proj;
|
const float extension_x = ghw + proj;
|
||||||
|
|
||||||
// upper cut plane is trapezium
|
// upper cut plane is trapezium
|
||||||
@ -793,7 +793,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width
|
|||||||
|
|
||||||
// upper cut plane is triangle
|
// upper cut plane is triangle
|
||||||
|
|
||||||
const float cross_pt_y = ghw / tan(m_groove_angle);
|
const float cross_pt_y = ghw / tan(m_groove.angle);
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
// roof
|
// roof
|
||||||
@ -814,14 +814,14 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width
|
|||||||
|
|
||||||
indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width)
|
indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width)
|
||||||
{
|
{
|
||||||
const float ghw_upper = 0.5f * (m_groove_width); // groove half width
|
const float ghw_upper = 0.5f * (m_groove.width); // groove half width
|
||||||
const float ghw_lower = 0.5f * (m_groove_width + flaps_width); // groove half width
|
const float ghw_lower = 0.5f * (m_groove.width + flaps_width); // groove half width
|
||||||
const float cpr = 1.5f * float(m_radius); // cut plane radius
|
const float cpr = 1.5f * float(m_radius); // cut plane radius
|
||||||
|
|
||||||
const float cpl = 1.5f * cpr; // cut plane length
|
const float cpl = 1.5f * cpr; // cut plane length
|
||||||
const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height
|
const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height
|
||||||
|
|
||||||
const float ghd = 0.5f * m_groove_depth; // groove half depth
|
const float ghd = 0.5f * m_groove.depth; // groove half depth
|
||||||
|
|
||||||
|
|
||||||
// We need two upward facing triangles
|
// We need two upward facing triangles
|
||||||
@ -829,7 +829,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width
|
|||||||
float z_upper = ghd;
|
float z_upper = ghd;
|
||||||
float z_lower = -ghd;
|
float z_lower = -ghd;
|
||||||
|
|
||||||
const float proj = y * tan(m_groove_angle);
|
const float proj = y * tan(m_groove.angle);
|
||||||
|
|
||||||
const float extension_upper_x = ghw_upper + proj;
|
const float extension_upper_x = ghw_upper + proj;
|
||||||
const float extension_lower_x = ghw_lower + proj;
|
const float extension_lower_x = ghw_lower + proj;
|
||||||
@ -854,12 +854,12 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const float cross_pt_upper_y = ghw_upper / tan(m_groove_angle);
|
const float cross_pt_upper_y = ghw_upper / tan(m_groove.angle);
|
||||||
|
|
||||||
// groove is closed
|
// groove is closed
|
||||||
|
|
||||||
if (ghw_upper < proj && ghw_lower < proj) {
|
if (ghw_upper < proj && ghw_lower < proj) {
|
||||||
const float cross_pt_lower_y = ghw_lower / tan(m_groove_angle);
|
const float cross_pt_lower_y = ghw_lower / tan(m_groove.angle);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
@ -898,23 +898,23 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane()
|
|||||||
{
|
{
|
||||||
// values for calculation
|
// values for calculation
|
||||||
|
|
||||||
const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle));
|
const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle));
|
||||||
const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle);
|
const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle);
|
||||||
|
|
||||||
const float groove_half_width_upper = 0.5f * (m_groove_width);
|
const float groove_half_width_upper = 0.5f * (m_groove.width);
|
||||||
const float groove_half_width_lower = 0.5f * (m_groove_width + flaps_width);
|
const float groove_half_width_lower = 0.5f * (m_groove.width + flaps_width);
|
||||||
|
|
||||||
const float cut_plane_radius = 1.5f * float(m_radius);
|
const float cut_plane_radius = 1.5f * float(m_radius);
|
||||||
const float cut_plane_length = 1.5f * cut_plane_radius;
|
const float cut_plane_length = 1.5f * cut_plane_radius;
|
||||||
|
|
||||||
const float groove_half_depth = 0.5f * m_groove_depth;
|
const float groove_half_depth = 0.5f * m_groove.depth;
|
||||||
|
|
||||||
const float x = 0.5f * cut_plane_radius;
|
const float x = 0.5f * cut_plane_radius;
|
||||||
const float y = 0.5f * cut_plane_length;
|
const float y = 0.5f * cut_plane_length;
|
||||||
float z_upper = groove_half_depth;
|
float z_upper = groove_half_depth;
|
||||||
float z_lower = -groove_half_depth;
|
float z_lower = -groove_half_depth;
|
||||||
|
|
||||||
const float proj = y * tan(m_groove_angle);
|
const float proj = y * tan(m_groove.angle);
|
||||||
|
|
||||||
float ext_upper_x = groove_half_width_upper + proj; // upper_x extension
|
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 ext_lower_x = groove_half_width_lower + proj; // lower_x extension
|
||||||
@ -966,7 +966,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane()
|
|||||||
z_upper -= cut_plane_thiknes;
|
z_upper -= cut_plane_thiknes;
|
||||||
z_lower -= cut_plane_thiknes;
|
z_lower -= cut_plane_thiknes;
|
||||||
if (m_use_TAG_mesh_full) {
|
if (m_use_TAG_mesh_full) {
|
||||||
const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle);
|
const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle);
|
||||||
|
|
||||||
nar_upper_x += under_x_shift;
|
nar_upper_x += under_x_shift;
|
||||||
nar_lower_x += under_x_shift;
|
nar_lower_x += under_x_shift;
|
||||||
@ -1019,12 +1019,12 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane()
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
float cross_pt_upper_y = groove_half_width_upper / tan(m_groove_angle);
|
float cross_pt_upper_y = groove_half_width_upper / tan(m_groove.angle);
|
||||||
|
|
||||||
// groove is closed
|
// groove is closed
|
||||||
|
|
||||||
if (groove_half_width_upper < proj && groove_half_width_lower < proj) {
|
if (groove_half_width_upper < proj && groove_half_width_lower < proj) {
|
||||||
float cross_pt_lower_y = groove_half_width_lower / tan(m_groove_angle);
|
float cross_pt_lower_y = groove_half_width_lower / tan(m_groove.angle);
|
||||||
|
|
||||||
indexed_triangle_set mesh;
|
indexed_triangle_set mesh;
|
||||||
|
|
||||||
@ -1045,7 +1045,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane()
|
|||||||
z_lower -= cut_plane_thiknes;
|
z_lower -= cut_plane_thiknes;
|
||||||
|
|
||||||
if (m_use_TAG_mesh_full) {
|
if (m_use_TAG_mesh_full) {
|
||||||
const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle);
|
const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle);
|
||||||
|
|
||||||
cross_pt_upper_y += cut_plane_thiknes;
|
cross_pt_upper_y += cut_plane_thiknes;
|
||||||
cross_pt_lower_y += cut_plane_thiknes;
|
cross_pt_lower_y += cut_plane_thiknes;
|
||||||
@ -1112,7 +1112,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane()
|
|||||||
z_upper -= cut_plane_thiknes;
|
z_upper -= cut_plane_thiknes;
|
||||||
z_lower -= cut_plane_thiknes;
|
z_lower -= cut_plane_thiknes;
|
||||||
|
|
||||||
const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle);
|
const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle);
|
||||||
|
|
||||||
nar_lower_x += under_x_shift;
|
nar_lower_x += under_x_shift;
|
||||||
ext_upper_x += under_x_shift;
|
ext_upper_x += under_x_shift;
|
||||||
@ -1173,10 +1173,10 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade
|
|||||||
ColorRGBA cp_clr = m_plane.model.get_color();
|
ColorRGBA cp_clr = m_plane.model.get_color();
|
||||||
|
|
||||||
// values for calculaton
|
// values for calculaton
|
||||||
const double groove_half_depth = 0.5 * double(m_groove_depth);
|
const double groove_half_depth = 0.5 * double(m_groove.depth);
|
||||||
|
|
||||||
const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle));
|
const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle));
|
||||||
const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle);
|
const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle);
|
||||||
|
|
||||||
GLModel model;
|
GLModel model;
|
||||||
|
|
||||||
@ -1454,7 +1454,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
|||||||
|
|
||||||
// render CutPlaneYMove grabber
|
// render CutPlaneYMove grabber
|
||||||
|
|
||||||
if (m_groove_angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove))
|
if (m_groove.angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove))
|
||||||
{
|
{
|
||||||
size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size));
|
size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size));
|
||||||
color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color();
|
color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color();
|
||||||
@ -1526,19 +1526,19 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar)
|
|||||||
if (m_mode != mode)
|
if (m_mode != mode)
|
||||||
switch_to_mode(mode);
|
switch_to_mode(mode);
|
||||||
else if (CutMode(m_mode) == CutMode::cutTongueAndGroove) {
|
else if (CutMode(m_mode) == CutMode::cutTongueAndGroove) {
|
||||||
if (!is_approx(m_groove_depth , groove_depth) ||
|
if (!is_approx(m_groove.depth , groove_depth) ||
|
||||||
!is_approx(m_groove_width , groove_width) ||
|
!is_approx(m_groove.width , groove_width) ||
|
||||||
!is_approx(m_groove_flaps_angle , groove_flaps_angle) ||
|
!is_approx(m_groove.flaps_angle , groove_flaps_angle) ||
|
||||||
!is_approx(m_groove_angle , groove_angle) ||
|
!is_approx(m_groove.angle , groove_angle) ||
|
||||||
!is_approx(m_groove_depth_tolerance, groove_depth_tolerance) ||
|
!is_approx(m_groove.depth_tolerance, groove_depth_tolerance) ||
|
||||||
!is_approx(m_groove_width_tolerance, groove_width_tolerance) )
|
!is_approx(m_groove.width_tolerance, groove_width_tolerance) )
|
||||||
{
|
{
|
||||||
m_groove_depth = groove_depth;
|
m_groove.depth = groove_depth;
|
||||||
m_groove_width = groove_width;
|
m_groove.width = groove_width;
|
||||||
m_groove_flaps_angle = groove_flaps_angle;
|
m_groove.flaps_angle = groove_flaps_angle;
|
||||||
m_groove_angle = groove_angle;
|
m_groove.angle = groove_angle;
|
||||||
m_groove_depth_tolerance= groove_depth_tolerance;
|
m_groove.depth_tolerance= groove_depth_tolerance;
|
||||||
m_groove_width_tolerance= groove_width_tolerance;
|
m_groove.width_tolerance= groove_width_tolerance;
|
||||||
update_plane_model();
|
update_plane_model();
|
||||||
}
|
}
|
||||||
reset_cut_by_contours();
|
reset_cut_by_contours();
|
||||||
@ -1551,7 +1551,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,
|
ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,
|
||||||
m_ar_plane_center, m_start_dragging_m,
|
m_ar_plane_center, m_start_dragging_m,
|
||||||
m_groove_depth, m_groove_width, m_groove_flaps_angle, m_groove_angle, m_groove_depth_tolerance, m_groove_width_tolerance);
|
m_groove.depth, m_groove.width, m_groove.flaps_angle, m_groove.angle, m_groove.depth_tolerance, m_groove.width_tolerance);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GLGizmoCut3D::on_get_name() const
|
std::string GLGizmoCut3D::on_get_name() const
|
||||||
@ -1755,7 +1755,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform()
|
|||||||
offset = (size + xy_connection_len) * Vec3d::UnitX();
|
offset = (size + xy_connection_len) * Vec3d::UnitX();
|
||||||
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale));
|
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale));
|
||||||
|
|
||||||
if (m_groove_angle > 0.0f) {
|
if (m_groove.angle > 0.0f) {
|
||||||
offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones();
|
offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones();
|
||||||
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size));
|
m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size));
|
||||||
offset = (size + xy_connection_len) * Vec3d::UnitY();
|
offset = (size + xy_connection_len) * Vec3d::UnitY();
|
||||||
@ -1996,7 +1996,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=fa
|
|||||||
|
|
||||||
bool can_set_center_pos = false;
|
bool can_set_center_pos = false;
|
||||||
{
|
{
|
||||||
double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove_depth) : */0.5;
|
double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove.depth) : */0.5;
|
||||||
if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val)
|
if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val)
|
||||||
can_set_center_pos = true;
|
can_set_center_pos = true;
|
||||||
else {
|
else {
|
||||||
@ -2093,10 +2093,10 @@ void GLGizmoCut3D::update_bb()
|
|||||||
m_snap_fine_out_radius = m_grabber_connection_len * 1.15;
|
m_snap_fine_out_radius = m_grabber_connection_len * 1.15;
|
||||||
|
|
||||||
// input params for cut with tongue and groove
|
// input params for cut with tongue and groove
|
||||||
m_groove_depth = m_groove_depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box)));
|
m_groove.depth = m_groove.depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box)));
|
||||||
m_groove_width = m_groove_width_init = 4.0f * m_groove_depth;
|
m_groove.width = m_groove.width_init = 4.0f * m_groove.depth;
|
||||||
m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f;
|
m_groove.flaps_angle = m_groove.flaps_angle_init = float(PI) / 3.f;
|
||||||
m_groove_angle = m_groove_angle_init = 0.f;
|
m_groove.angle = m_groove.angle_init = 0.f;
|
||||||
m_plane.reset();
|
m_plane.reset();
|
||||||
m_cone.reset();
|
m_cone.reset();
|
||||||
m_sphere.reset();
|
m_sphere.reset();
|
||||||
@ -2188,16 +2188,9 @@ void GLGizmoCut3D::render_clipper_cut()
|
|||||||
|
|
||||||
GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc)
|
GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc)
|
||||||
{
|
{
|
||||||
|
Cut cut(mo, instance_idx_in, cut_matrix);
|
||||||
m_model = Model();
|
m_model = Model();
|
||||||
m_model.add_object(*mo);
|
m_model.add_object(*cut.perform_with_plane().front());
|
||||||
|
|
||||||
Model tmp_model = Model();
|
|
||||||
tmp_model.objects = m_model.objects.front()->cut(instance_idx_in, cut_matrix,
|
|
||||||
ModelObjectCutAttribute::KeepUpper |
|
|
||||||
ModelObjectCutAttribute::KeepLower |
|
|
||||||
ModelObjectCutAttribute::KeepAsParts);
|
|
||||||
assert(tmp_model.objects.size() == 1);
|
|
||||||
m_model = tmp_model;
|
|
||||||
|
|
||||||
m_instance_idx = instance_idx_in;
|
m_instance_idx = instance_idx_in;
|
||||||
|
|
||||||
@ -2270,6 +2263,7 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor
|
|||||||
m_valid = true;
|
m_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In CutMode::cutTongueAndGroove we use PartSelection just for rendering
|
||||||
GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in)
|
GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in)
|
||||||
{
|
{
|
||||||
m_instance_idx = instance_idx_in;
|
m_instance_idx = instance_idx_in;
|
||||||
@ -2388,6 +2382,16 @@ bool GLGizmoCut3D::PartSelection::is_one_object() const
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Cut::Part> GLGizmoCut3D::PartSelection::get_cut_parts()
|
||||||
|
{
|
||||||
|
std::vector<Cut::Part> parts;
|
||||||
|
|
||||||
|
for (const auto& part : m_parts)
|
||||||
|
parts.push_back({part.selected, part.is_modifier});
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
|
void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
|
||||||
{
|
{
|
||||||
@ -2795,11 +2799,12 @@ void GLGizmoCut3D::process_contours()
|
|||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
|
|
||||||
if (CutMode(m_mode) == CutMode::cutTongueAndGroove) {
|
if (CutMode(m_mode) == CutMode::cutTongueAndGroove) {
|
||||||
Model tmp_model = Model();
|
if (has_valid_groove()) {
|
||||||
tmp_model.objects = perform_cut_with_groove(model_objects[object_idx], true);
|
Cut cut(model_objects[object_idx], instance_idx, get_cut_matrix(selection));
|
||||||
if (!tmp_model.objects.empty())
|
const ModelObjectPtrs& new_objects = cut.perform_with_groove(m_groove, m_rotation_m, true);
|
||||||
m_part_selection = PartSelection(tmp_model.objects.front(), instance_idx);
|
if (!new_objects.empty())
|
||||||
tmp_model = Model();
|
m_part_selection = PartSelection(new_objects.front(), instance_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
reset_cut_by_contours();
|
reset_cut_by_contours();
|
||||||
@ -2989,10 +2994,10 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||||||
else if (mode == CutMode::cutTongueAndGroove) {
|
else if (mode == CutMode::cutTongueAndGroove) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": ");
|
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": ");
|
||||||
render_groove_float_input(m_labels_map["Depth"], m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance);
|
render_groove_float_input(m_labels_map["Depth"], m_groove.depth, m_groove.depth_init, m_groove.depth_tolerance);
|
||||||
render_groove_float_input(m_labels_map["Width"], m_groove_width, m_groove_width_init, m_groove_width_tolerance);
|
render_groove_float_input(m_labels_map["Width"], m_groove.width, m_groove.width_init, m_groove.width_tolerance);
|
||||||
render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove_flaps_angle, m_groove_flaps_angle_init, 30.f, 120.f);
|
render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove.flaps_angle, m_groove.flaps_angle_init, 30.f, 120.f);
|
||||||
render_groove_angle_input(m_labels_map["Groove Angle"], m_groove_angle, m_groove_angle_init, 0.f, 15.f);
|
render_groove_angle_input(m_labels_map["Groove Angle"], m_groove.angle, m_groove.angle_init, 0.f, 15.f);
|
||||||
// m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering);
|
// m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3445,8 +3450,8 @@ bool GLGizmoCut3D::has_valid_groove() const
|
|||||||
if (CutMode(m_mode) != CutMode::cutTongueAndGroove)
|
if (CutMode(m_mode) != CutMode::cutTongueAndGroove)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle);
|
const float flaps_width = -2.f * m_groove.depth / tan(m_groove.flaps_angle);
|
||||||
if (flaps_width > m_groove_width)
|
if (flaps_width > m_groove.width)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const Selection& selection = m_parent.get_selection();
|
const Selection& selection = m_parent.get_selection();
|
||||||
@ -3553,280 +3558,6 @@ void synchronize_model_after_cut(Model& model, const CutObjectBase& cut_id)
|
|||||||
obj->cut_id.copy(cut_id);
|
obj->cut_id.copy(cut_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count)
|
|
||||||
{
|
|
||||||
const Selection& selection = m_parent.get_selection();
|
|
||||||
const int instance_idx = selection.get_instance_idx();
|
|
||||||
|
|
||||||
// Clone the object to duplicate instances, materials etc.
|
|
||||||
ModelObject* upper{ nullptr };
|
|
||||||
if (m_keep_upper) cut_mo->clone_for_cut(&upper);
|
|
||||||
ModelObject* lower{ nullptr };
|
|
||||||
if (m_keep_lower) cut_mo->clone_for_cut(&lower);
|
|
||||||
|
|
||||||
const Transform3d cut_matrix = get_cut_matrix(selection);
|
|
||||||
|
|
||||||
auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) {
|
|
||||||
if (upper && !upper->volumes.empty()) {
|
|
||||||
ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper);
|
|
||||||
cut_objects.push_back(upper);
|
|
||||||
}
|
|
||||||
if (lower && !lower->volumes.empty()) {
|
|
||||||
ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower);
|
|
||||||
cut_objects.push_back(lower);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t cut_parts_cnt = m_part_selection.parts().size();
|
|
||||||
bool has_modifiers = false;
|
|
||||||
|
|
||||||
// Distribute SolidParts to the Upper/Lower object
|
|
||||||
for (size_t id = 0; id < cut_parts_cnt; ++id) {
|
|
||||||
if (m_part_selection.parts()[id].is_modifier)
|
|
||||||
has_modifiers = true; // modifiers will be added later to the related parts
|
|
||||||
else if (ModelObject* obj = (m_part_selection.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, instance_idx, 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
|
|
||||||
add_cut_objects(cut_object_ptrs, upper, lower);
|
|
||||||
}
|
|
||||||
else if (volumes.size() > cut_parts_cnt) {
|
|
||||||
// Means that object is cut with connectors
|
|
||||||
|
|
||||||
// All volumes are distributed to Upper / Lower object,
|
|
||||||
// So we don’t 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
|
|
||||||
const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes);
|
|
||||||
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
|
|
||||||
add_cut_objects(cut_object_ptrs, upper, lower);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
return cut_object_ptrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts/* = false*/)
|
|
||||||
{
|
|
||||||
if (!has_valid_groove())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const Selection& selection = m_parent.get_selection();
|
|
||||||
const int instance_idx = selection.get_instance_idx();
|
|
||||||
|
|
||||||
// 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(m_groove_depth);
|
|
||||||
|
|
||||||
const Transform3d cut_matrix = get_cut_matrix(selection);
|
|
||||||
|
|
||||||
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 = [instance_idx, add_volumes_from_cut]
|
|
||||||
(ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) {
|
|
||||||
Model model = Model();
|
|
||||||
model.add_object(*object);
|
|
||||||
|
|
||||||
tmp_model_for_cut = Model();
|
|
||||||
tmp_model_for_cut.objects = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts);
|
|
||||||
assert(!tmp_model_for_cut.objects.empty());
|
|
||||||
|
|
||||||
object->clear_volumes();
|
|
||||||
add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut);
|
|
||||||
ModelObject::reset_instance_transformation(object, instance_idx);
|
|
||||||
};
|
|
||||||
|
|
||||||
// cut by upper plane
|
|
||||||
|
|
||||||
const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * 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(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * 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(m_groove_width + m_groove_depth / tan(m_groove_flaps_angle));
|
|
||||||
|
|
||||||
// cut by angle1 plane
|
|
||||||
{
|
|
||||||
const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_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(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_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)m_groove_depth_tolerance;
|
|
||||||
|
|
||||||
const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * 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(m_groove_width_tolerance);
|
|
||||||
|
|
||||||
const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle));
|
|
||||||
cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
|
||||||
|
|
||||||
const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_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, instance_idx, upper, lower);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!upper->volumes.empty() && !lower->volumes.empty());
|
|
||||||
|
|
||||||
ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix_upper, m_place_on_cut_upper, m_rotate_upper);
|
|
||||||
cut_object_ptrs.push_back(upper);
|
|
||||||
ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix_lower, m_place_on_cut_upper, m_rotate_upper);
|
|
||||||
cut_object_ptrs.push_back(lower);
|
|
||||||
|
|
||||||
// Now merge all model parts together:
|
|
||||||
merge_solid_parts_inside_object(cut_object_ptrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cut_object_ptrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLGizmoCut3D::perform_cut(const Selection& selection)
|
void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||||
{
|
{
|
||||||
if (!can_perform_cut())
|
if (!can_perform_cut())
|
||||||
@ -3867,8 +3598,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
|||||||
|
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
|
|
||||||
const Transform3d cut_matrix = get_cut_matrix(selection);
|
|
||||||
|
|
||||||
ModelObjectCutAttributes attributes = only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
|
ModelObjectCutAttributes attributes = only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
|
||||||
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
|
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
|
||||||
only_if(has_connectors ? false : m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) |
|
only_if(has_connectors ? false : m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) |
|
||||||
@ -3882,16 +3611,15 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
|||||||
// update cut_id for the cut object in respect to the attributes
|
// update cut_id for the cut object in respect to the attributes
|
||||||
update_object_cut_id(cut_mo->cut_id, attributes, dowels_count);
|
update_object_cut_id(cut_mo->cut_id, attributes, dowels_count);
|
||||||
|
|
||||||
Model tmp_model = Model();
|
Cut cut(cut_mo, instance_idx, get_cut_matrix(selection), attributes);
|
||||||
tmp_model.objects = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) :
|
const ModelObjectPtrs& new_objects = cut_by_contour ? cut.perform_by_contour(m_part_selection.get_cut_parts(), dowels_count):
|
||||||
cut_with_groove ? perform_cut_with_groove(cut_mo) :
|
cut_with_groove ? cut.perform_with_groove(m_groove, m_rotation_m) :
|
||||||
cut_mo->cut(instance_idx, cut_matrix, attributes);
|
cut.perform_with_plane();
|
||||||
|
|
||||||
// save cut_id to post update synchronization
|
// save cut_id to post update synchronization
|
||||||
const CutObjectBase cut_id = cut_mo->cut_id;
|
const CutObjectBase cut_id = cut_mo->cut_id;
|
||||||
|
|
||||||
// update cut results on plater and in the model
|
// update cut results on plater and in the model
|
||||||
plater->cut(object_idx, tmp_model.objects);
|
plater->apply_cut_object_to_model(object_idx, new_objects);
|
||||||
|
|
||||||
synchronize_model_after_cut(plater->model(), cut_id);
|
synchronize_model_after_cut(plater->model(), cut_id);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "libslic3r/TriangleMesh.hpp"
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/CutUtils.hpp"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -118,16 +119,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
bool m_rotate_lower{ false };
|
bool m_rotate_lower{ false };
|
||||||
|
|
||||||
// Input params for cut with tongue and groove
|
// Input params for cut with tongue and groove
|
||||||
float m_groove_depth;
|
Cut::Groove m_groove;
|
||||||
float m_groove_width;
|
|
||||||
float m_groove_flaps_angle;
|
|
||||||
float m_groove_angle;
|
|
||||||
float m_groove_depth_init;
|
|
||||||
float m_groove_width_init;
|
|
||||||
float m_groove_flaps_angle_init;
|
|
||||||
float m_groove_angle_init;
|
|
||||||
float m_groove_depth_tolerance{ 0.1f };
|
|
||||||
float m_groove_width_tolerance{ 0.1f };
|
|
||||||
bool m_optimaze_groove_rendering{ true };
|
bool m_optimaze_groove_rendering{ true };
|
||||||
|
|
||||||
// Input params for cut with snaps
|
// Input params for cut with snaps
|
||||||
@ -187,6 +179,8 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
const std::vector<Part>& parts() const { return m_parts; }
|
const std::vector<Part>& parts() const { return m_parts; }
|
||||||
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
|
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
|
||||||
|
|
||||||
|
std::vector<Cut::Part> get_cut_parts();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model m_model;
|
Model m_model;
|
||||||
int m_instance_idx;
|
int m_instance_idx;
|
||||||
@ -367,8 +361,6 @@ private:
|
|||||||
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0);
|
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0);
|
||||||
void render_cut_plane_grabbers();
|
void render_cut_plane_grabbers();
|
||||||
void render_cut_line();
|
void render_cut_line();
|
||||||
ModelObjectPtrs perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count);
|
|
||||||
ModelObjectPtrs perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts = false);
|
|
||||||
void perform_cut(const Selection&selection);
|
void perform_cut(const Selection&selection);
|
||||||
void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false);
|
void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false);
|
||||||
void update_bb();
|
void update_bb();
|
||||||
|
@ -6318,20 +6318,7 @@ void Plater::toggle_layers_editing(bool enable)
|
|||||||
canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting"));
|
canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
|
void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& new_objects)
|
||||||
{
|
|
||||||
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
|
|
||||||
auto* object = p->model.objects[obj_idx];
|
|
||||||
|
|
||||||
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds");
|
|
||||||
|
|
||||||
wxBusyCursor wait;
|
|
||||||
|
|
||||||
const auto new_objects = object->cut(instance_idx, cut_matrix, attributes);
|
|
||||||
cut(obj_idx, new_objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plater::cut(size_t obj_idx, const ModelObjectPtrs& new_objects)
|
|
||||||
{
|
{
|
||||||
model().delete_object(obj_idx);
|
model().delete_object(obj_idx);
|
||||||
sidebar().obj_list()->delete_object_from_list(obj_idx);
|
sidebar().obj_list()->delete_object_from_list(obj_idx);
|
||||||
|
@ -264,8 +264,7 @@ public:
|
|||||||
void convert_unit(ConversionType conv_type);
|
void convert_unit(ConversionType conv_type);
|
||||||
void toggle_layers_editing(bool enable);
|
void toggle_layers_editing(bool enable);
|
||||||
|
|
||||||
void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes);
|
void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
|
||||||
void cut(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
|
|
||||||
|
|
||||||
void export_gcode(bool prefer_removable);
|
void export_gcode(bool prefer_removable);
|
||||||
void export_stl_obj(bool extended = false, bool selection_only = false);
|
void export_stl_obj(bool extended = false, bool selection_only = false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user