Cut: Initial porting of Cut Gizmo

This commit is contained in:
enricoturri1966 2023-10-31 23:01:05 +08:00 committed by Noisyfox
parent ce2836a7f9
commit 18406c31c0
34 changed files with 5638 additions and 1361 deletions

View file

@ -1553,23 +1553,11 @@ int CLI::run(int argc, char **argv)
o->cut(Z, m_config.opt_float("cut"), &out); o->cut(Z, m_config.opt_float("cut"), &out);
} }
#else #else
ModelObject* object = model.objects.front(); Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
const BoundingBoxf3& box = object->bounding_box(); ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
const float Margin = 20.0; auto cut_objects = cut.perform_with_plane();
const float max_x = box.size()(0) / 2.0 + Margin; for (ModelObject* obj : cut_objects)
const float min_x = -max_x; model.add_object(*obj);
const float max_y = box.size()(1) / 2.0 + Margin;
const float min_y = -max_y;
std::array<Vec3d, 4> plane_points;
plane_points[0] = { min_x, min_y, 0 };
plane_points[1] = { max_x, min_y, 0 };
plane_points[2] = { max_x, max_y, 0 };
plane_points[3] = { min_x, max_y, 0 };
for (Vec3d& point : plane_points) {
point += box.center();
}
model.objects.front()->cut(0, plane_points, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower);
#endif #endif
model.delete_object(size_t(0)); model.delete_object(size_t(0));
} }

View file

@ -153,7 +153,9 @@ namespace ImGui
// const wchar_t CustomSupportsMarker = 0x1D; // const wchar_t CustomSupportsMarker = 0x1D;
// const wchar_t CustomSeamMarker = 0x1E; // const wchar_t CustomSeamMarker = 0x1E;
// const wchar_t MmuSegmentationMarker = 0x1F; // const wchar_t MmuSegmentationMarker = 0x1F;
const wchar_t PlugMarker = 0x1E;
const wchar_t DowelMarker = 0x1F;
const wchar_t SnapMarker = 0x20;
// Do not forget use following letters only in wstring // Do not forget use following letters only in wstring
//BBS use 08xx to avoid unicode character which may be used //BBS use 08xx to avoid unicode character which may be used
const wchar_t DocumentationButton = 0x0800; const wchar_t DocumentationButton = 0x0800;
@ -198,6 +200,7 @@ namespace ImGui
const wchar_t CloseBlockNotifHoverButton = 0x0834; const wchar_t CloseBlockNotifHoverButton = 0x0834;
const wchar_t BlockNotifErrorIcon = 0x0835; const wchar_t BlockNotifErrorIcon = 0x0835;
const wchar_t ClipboardBtnDarkIcon = 0x0836; const wchar_t ClipboardBtnDarkIcon = 0x0836;
const wchar_t InfoMarkerSmall = 0x0837;
// void MyFunction(const char* name, const MyMatrix44& v); // void MyFunction(const char* name, const MyMatrix44& v);
} }

View file

@ -202,6 +202,8 @@ set(lisbslic3r_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

663
src/libslic3r/CutUtils.cpp Normal file
View file

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

View file

@ -0,0 +1,74 @@
///|/ Copyright (c) Prusa Research 2023 Oleksandra Iushchenko @YuSanka
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_CutUtils_hpp_
#define slic3r_CutUtils_hpp_
#include "enum_bitmask.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;
void post_process(ModelObject* object, ModelObjectPtrs& objects, bool keep, bool place_on_cut, bool flip);
void post_process(ModelObject* upper_object, ModelObject* lower_object, ModelObjectPtrs& objects);
void finalize(const ModelObjectPtrs& objects);
public:
Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper |
ModelObjectCutAttribute::KeepLower |
ModelObjectCutAttribute::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);
static ModelObjectPtrs cut_horizontal(const ModelObject *object, size_t instance_idx, double z, ModelObjectCutAttributes attributes);
}; // namespace Cut
} // namespace Slic3r
#endif /* slic3r_CutUtils_hpp_ */

View file

@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) 2020 Henner Zeller
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "../libslic3r.h" #include "../libslic3r.h"
#include "../Exception.hpp" #include "../Exception.hpp"
#include "../Model.hpp" #include "../Model.hpp"
@ -741,8 +746,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
{ {
int volume_id; int volume_id;
int type; int type;
float radius;
float height;
float r_tolerance; float r_tolerance;
float h_tolerance; float h_tolerance;
}; };
@ -758,10 +761,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//typedef std::map<Id, ComponentsList> IdToAliasesMap; //typedef std::map<Id, ComponentsList> IdToAliasesMap;
typedef std::vector<Instance> InstancesList; typedef std::vector<Instance> InstancesList;
typedef std::map<int, ObjectMetadata> IdToMetadataMap; typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
//typedef std::map<Id, Geometry> IdToGeometryMap; //typedef std::map<Id, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap; typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap; typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
/*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap; /*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;*/ typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;*/
@ -1013,7 +1016,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions); void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
@ -1955,11 +1958,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1); IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
if (cut_object_info != m_cut_object_infos.end()) { if (cut_object_info != m_cut_object_infos.end()) {
model_object->cut_id = cut_object_info->second.id; model_object->cut_id = cut_object_info->second.id;
int vol_cnt = int(model_object->volumes.size());
for (auto connector : cut_object_info->second.connectors) { for (auto connector : cut_object_info->second.connectors) {
assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size())); if (connector.volume_id < 0 || connector.volume_id >= vol_cnt) {
add_error("Invalid connector is found");
continue;
}
model_object->volumes[connector.volume_id]->cut_info = model_object->volumes[connector.volume_id]->cut_info =
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.radius, connector.height, connector.r_tolerance, connector.h_tolerance, true); ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true);
} }
} }
} }
@ -2324,7 +2330,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
void _BBS_3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions) void _BBS_3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions)
{ {
if (stat.m_uncomp_size > 0) { if (stat.m_uncomp_size > 0) {
std::string buffer((size_t) stat.m_uncomp_size, 0); std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void *) buffer.data(), (size_t) stat.m_uncomp_size, 0); mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void *) buffer.data(), (size_t) stat.m_uncomp_size, 0);
if (res == 0) { if (res == 0) {
add_error("Error while reading cut information data to buffer"); add_error("Error while reading cut information data to buffer");
@ -2335,7 +2341,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
pt::ptree objects_tree; pt::ptree objects_tree;
pt::read_xml(iss, objects_tree); pt::read_xml(iss, objects_tree);
for (const auto &object : objects_tree.get_child("objects")) { for (const auto& object : objects_tree.get_child("objects")) {
pt::ptree object_tree = object.second; pt::ptree object_tree = object.second;
int obj_idx = object_tree.get<int>("<xmlattr>.id", -1); int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
if (obj_idx <= 0) { if (obj_idx <= 0) {
@ -2352,27 +2358,30 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
CutObjectBase cut_id; CutObjectBase cut_id;
std::vector<CutObjectInfo::Connector> connectors; std::vector<CutObjectInfo::Connector> connectors;
for (const auto &obj_cut_info : object_tree) { for (const auto& obj_cut_info : object_tree) {
if (obj_cut_info.first == "cut_id") { if (obj_cut_info.first == "cut_id") {
pt::ptree cut_id_tree = obj_cut_info.second; pt::ptree cut_id_tree = obj_cut_info.second;
cut_id = CutObjectBase(ObjectID(cut_id_tree.get<size_t>("<xmlattr>.id")), cut_id_tree.get<size_t>("<xmlattr>.check_sum"), cut_id = CutObjectBase(ObjectID( cut_id_tree.get<size_t>("<xmlattr>.id")),
cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt")); cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt"));
} }
if (obj_cut_info.first == "connectors") { if (obj_cut_info.first == "connectors") {
pt::ptree cut_connectors_tree = obj_cut_info.second; pt::ptree cut_connectors_tree = obj_cut_info.second;
for (const auto &cut_connector : cut_connectors_tree) { for (const auto& cut_connector : cut_connectors_tree) {
if (cut_connector.first != "connector") continue; if (cut_connector.first != "connector")
continue;
pt::ptree connector_tree = cut_connector.second; pt::ptree connector_tree = cut_connector.second;
CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"), connector_tree.get<int>("<xmlattr>.type"), CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"),
connector_tree.get<float>("<xmlattr>.radius", 0.f), connector_tree.get<float>("<xmlattr>.height", 0.f), connector_tree.get<int>("<xmlattr>.type"),
connector_tree.get<float>("<xmlattr>.r_tolerance"), connector_tree.get<float>("<xmlattr>.h_tolerance")}; connector_tree.get<float>("<xmlattr>.r_tolerance"),
connector_tree.get<float>("<xmlattr>.h_tolerance")};
connectors.emplace_back(connector); connectors.emplace_back(connector);
} }
} }
} }
CutObjectInfo cut_info{cut_id, connectors}; CutObjectInfo cut_info {cut_id, connectors};
m_cut_object_infos.insert({obj_idx, cut_info}); m_cut_object_infos.insert({ obj_idx, cut_info });
} }
} }
} }
@ -5260,6 +5269,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//BBS: change volume to seperate objects //BBS: change volume to seperate objects
bool _add_mesh_to_object_stream(std::function<bool(std::string &, bool)> const &flush, ObjectData const &object_data) const; bool _add_mesh_to_object_stream(std::function<bool(std::string &, bool)> const &flush, ObjectData const &object_data) const;
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const; bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const;
bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
@ -5270,7 +5280,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//BBS: add project embedded preset files //BBS: add project embedded preset files
bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector<Preset*> project_presets); bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector<Preset*> project_presets);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false);
bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model);
bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config); bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config);
bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr); bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr);
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
@ -6704,6 +6713,69 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true; return true;
} }
bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
{
std::string out = "";
pt::ptree tree;
unsigned int object_cnt = 0;
for (const ModelObject* object : model.objects) {
object_cnt++;
if (!object->is_cut())
continue;
pt::ptree& obj_tree = tree.add("objects.object", "");
obj_tree.put("<xmlattr>.id", object_cnt);
// Store info for cut_id
pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
// store cut_id atributes
cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
int volume_idx = -1;
for (const ModelVolume* volume : object->volumes) {
++volume_idx;
if (volume->is_cut_connector()) {
pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
connectors_tree.put("<xmlattr>.volume_id", volume_idx);
connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
}
}
}
if (!tree.empty()) {
std::ostringstream oss;
pt::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
boost::replace_all(out, "><object", ">\n <object");
boost::replace_all(out, "><cut_id", ">\n <cut_id");
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
boost::replace_all(out, "><connectors", ">\n <connectors");
boost::replace_all(out, "></connectors>", ">\n </connectors>");
boost::replace_all(out, "><connector", ">\n <connector");
boost::replace_all(out, "></connector>", ">\n </connector>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add cut information file to archive");
return false;
}
}
return true;
}
bool _BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model) bool _BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
{ {
assert(is_decimal_separator_point()); assert(is_decimal_separator_point());
@ -7266,69 +7338,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true; return true;
} }
bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
{
std::string out = "";
pt::ptree tree;
unsigned int object_cnt = 0;
for (const ModelObject *object : model.objects) {
object_cnt++;
pt::ptree &obj_tree = tree.add("objects.object", "");
obj_tree.put("<xmlattr>.id", object_cnt);
// Store info for cut_id
pt::ptree &cut_id_tree = obj_tree.add("cut_id", "");
// store cut_id atributes
cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
int volume_idx = -1;
for (const ModelVolume *volume : object->volumes) {
++volume_idx;
if (volume->is_cut_connector()) {
pt::ptree &connectors_tree = obj_tree.add("connectors.connector", "");
connectors_tree.put("<xmlattr>.volume_id", volume_idx);
connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
connectors_tree.put("<xmlattr>.radius", volume->cut_info.radius);
connectors_tree.put("<xmlattr>.height", volume->cut_info.height);
connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
}
}
}
if (!tree.empty()) {
std::ostringstream oss;
pt::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
boost::replace_all(out, "><object", ">\n <object");
boost::replace_all(out, "><cut_id", ">\n <cut_id");
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
boost::replace_all(out, "><connectors", ">\n <connectors");
boost::replace_all(out, "></connectors>", ">\n </connectors>");
boost::replace_all(out, "><connector", ">\n <connector");
boost::replace_all(out, "></connector>", ">\n </connector>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void *) out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add cut information file to archive");
return false;
}
}
return true;
}
bool _BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config) bool _BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config)
{ {
std::stringstream stream; std::stringstream stream;

View file

@ -1,3 +1,17 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/Geometry.pm:
///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Jose Luis Perez Diez
///|/ Copyright (c) 2013 Anders Sundman
///|/ Copyright (c) 2013 Jesse Vincent
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r.h" #include "libslic3r.h"
#include "Exception.hpp" #include "Exception.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
@ -432,6 +446,14 @@ Vec3d extract_euler_angles(const Transform3d& transform)
return extract_euler_angles(m); return extract_euler_angles(m);
} }
static Transform3d extract_rotation_matrix(const Transform3d& trafo)
{
Matrix3d rotation;
Matrix3d scale;
trafo.computeRotationScaling(&rotation, &scale);
return Transform3d(rotation);
}
void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix) void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix)
{ {
double epsilon = 1e-5; double epsilon = 1e-5;
@ -504,6 +526,11 @@ void Transformation::set_offset(Axis axis, double offset)
} }
} }
Transform3d Transformation::get_rotation_matrix() const
{
return extract_rotation_matrix(m_matrix);
}
void Transformation::set_rotation(const Vec3d& rotation) void Transformation::set_rotation(const Vec3d& rotation)
{ {
set_rotation(X, rotation.x()); set_rotation(X, rotation.x());

View file

@ -440,6 +440,8 @@ public:
const Vec3d& get_rotation() const { return m_rotation; } const Vec3d& get_rotation() const { return m_rotation; }
double get_rotation(Axis axis) const { return m_rotation(axis); } double get_rotation(Axis axis) const { return m_rotation(axis); }
Transform3d get_rotation_matrix() const;
void set_rotation(const Vec3d& rotation); void set_rotation(const Vec3d& rotation);
void set_rotation(Axis axis, double rotation); void set_rotation(Axis axis, double rotation);

View file

@ -1,3 +1,16 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2021 Boleslaw Ciesielski
///|/ Copyright (c) 2019 John Drake @foxox
///|/ Copyright (c) 2019 Sijmen Schoon
///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
///|/
///|/ ported from lib/Slic3r/Model.pm:
///|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966
///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Model.hpp" #include "Model.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "BuildVolume.hpp" #include "BuildVolume.hpp"
@ -1693,68 +1706,6 @@ bool ModelObject::has_connectors() const
return false; return false;
} }
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
int sectorCount {1};
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
break;
case CutConnectorShape::Square:
sectorCount = 4;
break;
case CutConnectorShape::Circle:
sectorCount = 360;
break;
case CutConnectorShape::Hexagon:
sectorCount = 6;
break;
default:
break;
}
if (connector_attributes.style == CutConnectorStyle::Prizm)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
return connector_mesh;
}
void ModelObject::apply_cut_connectors(const std::string &name)
{
if (cut_connectors.empty())
return;
using namespace Geometry;
size_t connector_id = cut_id.connectors_cnt();
for (const CutConnector &connector : cut_connectors) {
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
// Mesh will be centered when loading.
ModelVolume *new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
Transform3d translate_transform = Transform3d::Identity();
translate_transform.translate(connector.pos);
Transform3d scale_transform = Transform3d::Identity();
scale_transform.scale(Vec3f(connector.radius, connector.radius, connector.height).cast<double>());
// Transform the new modifier to be aligned inside the instance
new_volume->set_transformation(translate_transform * connector.rotation_m * scale_transform);
new_volume->cut_info = {connector.attribs.type, connector.radius, connector.height, connector.radius_tolerance, connector.height_tolerance};
new_volume->name = name + "-" + std::to_string(++connector_id);
}
cut_id.increase_connectors_cnt(cut_connectors.size());
// delete all connectors
cut_connectors.clear();
}
void ModelObject::invalidate_cut() void ModelObject::invalidate_cut()
{ {
this->cut_id.invalidate(); this->cut_id.invalidate();
@ -1770,43 +1721,10 @@ void ModelObject::delete_connectors()
} }
} }
void ModelObject::synchronize_model_after_cut()
{
for (ModelObject *obj : m_model->objects) {
if (obj == this || obj->cut_id.is_equal(this->cut_id)) continue;
if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id))
obj->cut_id.copy(this->cut_id);
}
}
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
{
// we don't save cut information, if result will not contains all parts of initial object
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) ||
!attributes.has(ModelObjectCutAttribute::KeepLower) ||
attributes.has(ModelObjectCutAttribute::InvalidateCutInfo))
return;
if (cut_id.id().invalid())
cut_id.init();
{
int cut_obj_cnt = -1;
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::KeepLower))
cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::CreateDowels))
cut_obj_cnt++;
if (cut_obj_cnt > 0)
cut_id.increase_check_sum(size_t(cut_obj_cnt));
}
}
void ModelObject::clone_for_cut(ModelObject **obj) void ModelObject::clone_for_cut(ModelObject **obj)
{ {
(*obj) = ModelObject::new_clone(*this); (*obj) = ModelObject::new_clone(*this);
(*obj)->set_model(nullptr); (*obj)->set_model(this->get_model());
(*obj)->sla_support_points.clear(); (*obj)->sla_support_points.clear();
(*obj)->sla_drain_holes.clear(); (*obj)->sla_drain_holes.clear();
(*obj)->sla_points_status = sla::PointsStatus::NoPoints; (*obj)->sla_points_status = sla::PointsStatus::NoPoints;
@ -1814,189 +1732,11 @@ void ModelObject::clone_for_cut(ModelObject **obj)
(*obj)->input_file.clear(); (*obj)->input_file.clear();
} }
Transform3d ModelObject::calculate_cut_plane_inverse_matrix(const std::array<Vec3d, 4>& plane_points) void ModelVolume::reset_extra_facets()
{ {
Vec3d mid_point = {0.0, 0.0, 0.0}; this->supported_facets.reset();
for (auto pt : plane_points) this->seam_facets.reset();
mid_point += pt; this->mmu_segmentation_facets.reset();
mid_point /= (double) plane_points.size();
Vec3d movement = -mid_point;
Vec3d v01 = plane_points[1] - plane_points[0];
Vec3d v12 = plane_points[2] - plane_points[1];
Vec3d plane_normal = v01.cross(v12);
plane_normal.normalize();
Vec3d axis = {0.0, 0.0, 0.0};
double phi = 0.0;
Matrix3d matrix;
matrix.setIdentity();
Geometry::rotation_from_two_vectors(plane_normal, {0.0, 0.0, 1.0}, axis, phi, &matrix);
Vec3d angles = Geometry::extract_euler_angles(matrix);
movement = matrix * movement;
Transform3d transfo;
transfo.setIdentity();
transfo.translate(movement);
transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX()));
return transfo;
}
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,
Vec3d &local_dowels_displace)
{
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 = 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);
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 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::CutToParts)) {
upper->add_volume(*volume);
return;
}
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut))
lower->add_volume(*volume);
}
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 Geometry::Transformation cut_transformation = Geometry::Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_matrix(true, false, true, true).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,
const std::array<Vec3d, 4> &plane_points,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower,
Vec3d & local_displace)
{
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
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);
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
// The upper part displacement is set to half of the lower part bounding box
// this is done in hope at least a part of the upper part will always be visible and draggable
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
}
} }
static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance) static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance)
@ -2073,215 +1813,6 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan
} }
} }
// BBS: replace z with plane_points
ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes)
{
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
return {};
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// apply cut attributes for object
apply_cut_attributes(attributes);
ModelObject* upper{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
clone_for_cut(&upper);
ModelObject* lower{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::CutToParts))
clone_for_cut(&lower);
// 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 = Geometry::assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), // BBS: do apply Z-rotation
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
// BBS
//z -= instances[instance]->get_offset().z();
for (Vec3d& point : plane_points) {
point -= instances[instance]->get_offset();
}
Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
Transform3d cut_matrix = inverse_cut_matrix.inverse();
std::vector<ModelObject *> dowels;
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
for (ModelVolume *volume : volumes) {
const auto volume_matrix = volume->get_matrix();
volume->supported_facets.reset();
volume->seam_facets.reset();
volume->mmu_segmentation_facets.reset();
if (! volume->is_model_part()) {
if (volume->cut_info.is_processed) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
//Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
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, local_dowels_displace);
}
}
else if (! volume->mesh().empty()) {
process_solid_part_cut(volume, instance_matrix, cut_matrix, plane_points, attributes, upper, lower, local_displace);
}
}
ModelObjectPtrs res;
if (attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) {
reset_instance_transformation(upper, instance, cut_matrix);
res.push_back(upper);
}
else {
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace);
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel);
}
}
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
synchronize_model_after_cut();
return res;
}
// BBS
ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders, double smoothing_alpha, int segment_number)
{
BOOST_LOG_TRIVIAL(trace) << "ModelObject::segment - start";
// Clone the object to duplicate instances, materials etc.
ModelObject* upper = ModelObject::new_clone(*this);
upper->set_model(nullptr);
upper->sla_support_points.clear();
upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes();
upper->input_file.clear();
// 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 = Geometry::assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation(), // BBS: keep Z-rotation
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
for (ModelVolume* volume : volumes) {
const auto volume_matrix = volume->get_matrix();
volume->supported_facets.reset();
volume->seam_facets.reset();
if (!volume->is_model_part()) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
upper->add_volume(*volume);
}
else if (!volume->mesh().empty()) {
// 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(instance_matrix * volume_matrix, true);
volume->reset_mesh();
auto mesh_segments = MeshBoolean::cgal::segment(mesh, smoothing_alpha, segment_number);
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
unsigned int extruder_counter = 0;
for (int idx=0;idx<mesh_segments.size();idx++)
{
auto& mesh_segment = mesh_segments[idx];
if (mesh_segment.facets_count() > 0) {
ModelVolume* vol = upper->add_volume(mesh_segment);
vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_" + std::to_string(idx);
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
#if 0
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
#else
vol->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
#endif
}
}
}
}
ModelObjectPtrs res;
if (upper->volumes.size() > 0) {
upper->invalidate_bounding_box();
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); i++) {
auto& instance = upper->instances[i];
const Vec3d offset = instance->get_offset();
// BBS
//const double rot_z = instance->get_rotation()(2);
instance->set_transformation(Geometry::Transformation());
instance->set_offset(offset);
// BBS
//instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
}
res.push_back(upper);
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::segment - end";
return res;
}
void ModelObject::split(ModelObjectPtrs* new_objects) void ModelObject::split(ModelObjectPtrs* new_objects)
{ {
std::vector<TriangleMesh> all_meshes; std::vector<TriangleMesh> all_meshes;
@ -2801,35 +2332,6 @@ bool ModelVolume::is_splittable() const
return m_is_splittable == 1; return m_is_splittable == 1;
} }
void ModelVolume::apply_tolerance()
{
assert(cut_info.is_connector);
if (!cut_info.is_processed)
return;
Vec3d sf = get_scaling_factor();
// make a "hole" wider
double size_scale = 1.f;
if (abs(cut_info.radius - 0) < EPSILON) // For compatibility with old files
size_scale = 1.f + double(cut_info.radius_tolerance);
else
size_scale = (double(cut_info.radius) + double(cut_info.radius_tolerance)) / double(cut_info.radius);
sf[X] *= size_scale;
sf[Y] *= size_scale;
// make a "hole" dipper
double height_scale = 1.f;
if (abs(cut_info.height - 0) < EPSILON) // For compatibility with old files
height_scale = 1.f + double(cut_info.height_tolerance);
else
height_scale = (double(cut_info.height) + double(cut_info.height_tolerance)) / double(cut_info.height);
sf[Z] *= height_scale;
set_scaling_factor(sf);
}
// BBS // BBS
std::vector<int> ModelVolume::get_extruders() const std::vector<int> ModelVolume::get_extruders() const
{ {

View file

@ -246,50 +246,56 @@ private:
}; };
enum class CutConnectorType : int { enum class CutConnectorType : int {
Plug, Plug
Dowel, , Dowel
Undef , Snap
, Undef
}; };
enum class CutConnectorStyle : int { enum class CutConnectorStyle : int {
Prizm, Prism
Frustum, , Frustum
Undef , Undef
//,Claw //,Claw
}; };
enum class CutConnectorShape : int { enum class CutConnectorShape : int {
Triangle, Triangle
Square, , Square
Hexagon, , Hexagon
Circle, , Circle
Undef , Undef
//,D-shape //,D-shape
}; };
struct CutConnectorAttributes struct CutConnectorAttributes
{ {
CutConnectorType type{CutConnectorType::Plug}; CutConnectorType type{ CutConnectorType::Plug };
CutConnectorStyle style{CutConnectorStyle::Prizm}; CutConnectorStyle style{ CutConnectorStyle::Prism };
CutConnectorShape shape{CutConnectorShape::Circle}; CutConnectorShape shape{ CutConnectorShape::Circle };
CutConnectorAttributes() {} CutConnectorAttributes() {}
CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) : type(t), style(st), shape(sh) {} CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh)
: type(t), style(st), shape(sh)
{}
CutConnectorAttributes(const CutConnectorAttributes &rhs) : CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} CutConnectorAttributes(const CutConnectorAttributes& rhs) :
CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {}
bool operator==(const CutConnectorAttributes &other) const; bool operator==(const CutConnectorAttributes& other) const;
bool operator!=(const CutConnectorAttributes &other) const { return !(other == (*this)); } bool operator!=(const CutConnectorAttributes& other) const { return !(other == (*this)); }
bool operator<(const CutConnectorAttributes &other) const bool operator<(const CutConnectorAttributes& other) const {
{ return this->type < other.type ||
return this->type < other.type || (this->type == other.type && this->style < other.style) || (this->type == other.type && this->style < other.style) ||
(this->type == other.type && this->style == other.style && this->shape < other.shape); (this->type == other.type && this->style == other.style && this->shape < other.shape);
} }
template<class Archive> inline void serialize(Archive &ar) { ar(type, style, shape); } template<class Archive> inline void serialize(Archive& ar) {
ar(type, style, shape);
}
}; };
struct CutConnector struct CutConnector
@ -298,27 +304,34 @@ struct CutConnector
Transform3d rotation_m; Transform3d rotation_m;
float radius; float radius;
float height; float height;
float radius_tolerance; // [0.f : 1.f] float radius_tolerance;// [0.f : 1.f]
float height_tolerance; // [0.f : 1.f] float height_tolerance;// [0.f : 1.f]
float z_angle {0.f};
CutConnectorAttributes attribs; CutConnectorAttributes attribs;
CutConnector() : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} CutConnector()
: pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f), z_angle(0.f)
CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes)
: pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes)
{} {}
CutConnector(const CutConnector &rhs) : CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, float za, CutConnectorAttributes attributes)
: pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), z_angle(za), attribs(attributes)
{}
bool operator==(const CutConnector &other) const; CutConnector(const CutConnector& rhs) :
CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.z_angle, rhs.attribs) {}
bool operator!=(const CutConnector &other) const { return !(other == (*this)); } bool operator==(const CutConnector& other) const;
template<class Archive> inline void serialize(Archive &ar) { ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); } bool operator!=(const CutConnector& other) const { return !(other == (*this)); }
template<class Archive> inline void serialize(Archive& ar) {
ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, z_angle, attribs);
}
}; };
using CutConnectors = std::vector<CutConnector>; using CutConnectors = std::vector<CutConnector>;
// Declared outside of ModelVolume, so it could be forward declared. // Declared outside of ModelVolume, so it could be forward declared.
enum class ModelVolumeType : int { enum class ModelVolumeType : int {
INVALID = -1, INVALID = -1,
@ -326,13 +339,9 @@ enum class ModelVolumeType : int {
NEGATIVE_VOLUME, NEGATIVE_VOLUME,
PARAMETER_MODIFIER, PARAMETER_MODIFIER,
SUPPORT_BLOCKER, SUPPORT_BLOCKER,
SUPPORT_ENFORCER SUPPORT_ENFORCER,
}; };
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, CutToParts, 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,
@ -371,6 +380,10 @@ public:
// Holes to be drilled into the object so resin can flow out // Holes to be drilled into the object so resin can flow out
sla::DrainHoles sla_drain_holes; sla::DrainHoles sla_drain_holes;
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
CutConnectors cut_connectors;
CutObjectBase cut_id;
/* This vector accumulates the total translation applied to the object by the /* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment to new volumes before adding them to this object in order to preserve alignment
@ -380,10 +393,6 @@ public:
// BBS: save for compare with new load volumes // BBS: save for compare with new load volumes
std::vector<ObjectID> volume_ids; std::vector<ObjectID> volume_ids;
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
CutConnectors cut_connectors;
CutObjectBase cut_id;
Model* get_model() { return m_model; } Model* get_model() { return m_model; }
const Model* get_model() const { return m_model; } const Model* get_model() const { return m_model; }
// BBS: production extension // BBS: production extension
@ -480,52 +489,13 @@ public:
size_t materials_count() const; size_t materials_count() const;
size_t facets_count() const; size_t facets_count() const;
size_t parts_count() const; size_t parts_count() const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(const std::string &name);
// invalidate cut state for this object and its connectors/volumes // invalidate cut state for this object and its connectors/volumes
void invalidate_cut(); void invalidate_cut();
// delete volumes which are marked as connector for this object // delete volumes which are marked as connector for this object
void delete_connectors(); void delete_connectors();
void synchronize_model_after_cut();
void apply_cut_attributes(ModelObjectCutAttributes attributes);
void clone_for_cut(ModelObject **obj); void clone_for_cut(ModelObject **obj);
Transform3d calculate_cut_plane_inverse_matrix(const std::array<Vec3d, 4> &plane_points);
void process_connector_cut(ModelVolume *volume,
const Transform3d & instance_matrix,
const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject *upper, ModelObject *lower,
std::vector<ModelObject *> &dowels,
Vec3d &local_dowels_displace);
void process_modifier_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & inverse_cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower);
void process_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,
const std::array<Vec3d, 4> &plane_points,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower,
Vec3d & local_displace);
// BBS: replace z with plane_points void split(ModelObjectPtrs*new_objects);
ModelObjectPtrs cut(size_t instance, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes);
// BBS
ModelObjectPtrs segment(size_t instance, unsigned int max_extruders, double smoothing_alpha = 0.5, int segment_number = 5);
void split(ModelObjectPtrs* new_objects);
void merge(); void merge();
// BBS: Boolean opts - Musang King // BBS: Boolean opts - Musang King
@ -553,6 +523,8 @@ public:
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_repaired_errors_count(const int vol_idx = -1) const; int get_repaired_errors_count(const int vol_idx = -1) const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
private: private:
friend class Model; friend class Model;
// This constructor assigns new ID to this ModelObject and its config. // This constructor assigns new ID to this ModelObject and its config.
@ -857,27 +829,35 @@ public:
// It contains information about connetors // It contains information about connetors
struct CutInfo struct CutInfo
{ {
bool is_connector{false}; bool is_from_upper{ true };
bool is_processed{true}; bool is_connector{ false };
CutConnectorType connector_type{CutConnectorType::Plug}; bool is_processed{ true };
float radius{0.f}; CutConnectorType connector_type{ CutConnectorType::Plug };
float height{0.f}; float radius_tolerance{ 0.f };// [0.f : 1.f]
float radius_tolerance{0.f}; // [0.f : 1.f] float height_tolerance{ 0.f };// [0.f : 1.f]
float height_tolerance{0.f}; // [0.f : 1.f]
CutInfo() = default; CutInfo() = default;
CutInfo(CutConnectorType type, float radius_, float height_, float rad_tolerance, float h_tolerance, bool processed = false) CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance, bool processed = false) :
: is_connector(true), is_processed(processed), connector_type(type) is_connector(true),
, radius(radius_), height(height_), radius_tolerance(rad_tolerance), height_tolerance(h_tolerance) is_processed(processed),
connector_type(type),
radius_tolerance(rad_tolerance),
height_tolerance(h_tolerance)
{} {}
void set_processed() { is_processed = true; } void set_processed() { is_processed = true; }
void invalidate() { is_connector = false; } void invalidate() { is_connector = false; }
void reset_from_upper() { is_from_upper = true; }
template<class Archive> inline void serialize(Archive &ar) { ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); } template<class Archive> inline void serialize(Archive& ar) {
ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance);
}
}; };
CutInfo cut_info; CutInfo cut_info;
bool is_from_upper() const { return cut_info.is_from_upper; }
void reset_from_upper() { cut_info.reset_from_upper(); }
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
void invalidate_cut_info() { cut_info.invalidate(); } void invalidate_cut_info() { cut_info.invalidate(); }
@ -922,6 +902,7 @@ public:
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
t_model_material_id material_id() const { return m_material_id; } t_model_material_id material_id() const { return m_material_id; }
void reset_extra_facets();
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);
@ -931,8 +912,6 @@ public:
bool is_splittable() const; bool is_splittable() const;
void apply_tolerance();
// BBS // BBS
std::vector<int> get_extruders() const; std::vector<int> get_extruders() const;
void update_extruder_count(size_t extruder_count); void update_extruder_count(size_t extruder_count);

View file

@ -1,3 +1,18 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2019 Jason Tibbitts @jasontibbitts
///|/ Copyright (c) 2019 Sijmen Schoon
///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
///|/ Copyright (c) 2014 Miro Hrončok @hroncok
///|/ Copyright (c) 2014 Petr Ledvina @ledvinap
///|/
///|/ ported from lib/Slic3r/TriangleMesh.pm:
///|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2012 - 2013 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Exception.hpp" #include "Exception.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "TriangleMeshSlicer.hpp" #include "TriangleMeshSlicer.hpp"
@ -958,6 +973,51 @@ indexed_triangle_set its_make_cylinder(double r, double h, double fa)
return mesh; return mesh;
} }
indexed_triangle_set its_make_frustum(double r, double h, double fa)
{
indexed_triangle_set mesh;
size_t n_steps = (size_t)ceil(2. * PI / fa);
double angle_step = 2. * PI / n_steps;
auto &vertices = mesh.vertices;
auto &facets = mesh.indices;
vertices.reserve(2 * n_steps + 2);
facets.reserve(4 * n_steps);
// 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Vec3f(0.f, 0.f, 0.f));
vertices.emplace_back(Vec3f(0.f, 0.f, float(h)));
// for each line along the polygon approximating the top/bottom of the
// circle, generate four points and four facets (2 for the wall, 2 for the
// top and bottom.
// Special case: Last line shares 2 vertices with the first line.
Vec2f vec_top = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, 0.5f*r);
Vec2f vec_botton = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, r);
vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f));
vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h)));
for (size_t i = 1; i < n_steps; ++i) {
vec_top = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, 0.5f*float(r));
vec_botton = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, float(r));
vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f));
vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h)));
int id = (int)vertices.size() - 1;
facets.emplace_back( 0, id - 1, id - 3); // top
facets.emplace_back(id, 1, id - 2); // bottom
facets.emplace_back(id, id - 2, id - 3); // upper-right of side
facets.emplace_back(id, id - 3, id - 1); // bottom-left of side
}
// Connect the last set of vertices with the first.
int id = (int)vertices.size() - 1;
facets.emplace_back( 0, 2, id - 1);
facets.emplace_back( 3, 1, id);
facets.emplace_back(id, 2, 3);
facets.emplace_back(id, id - 1, 2);
return mesh;
}
indexed_triangle_set its_make_cone(double r, double h, double fa) indexed_triangle_set its_make_cone(double r, double h, double fa)
{ {
indexed_triangle_set mesh; indexed_triangle_set mesh;
@ -984,61 +1044,6 @@ indexed_triangle_set its_make_cone(double r, double h, double fa)
return mesh; return mesh;
} }
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
// Note: This function uses code for sphere generation, but for stackCount = 2;
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
{
int stackCount = 2;
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
indexed_triangle_set mesh;
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j + 0.25 * M_PI;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return mesh;
}
indexed_triangle_set its_make_pyramid(float base, float height) indexed_triangle_set its_make_pyramid(float base, float height)
{ {
float a = base / 2.f; float a = base / 2.f;
@ -1116,6 +1121,182 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
return mesh; return mesh;
} }
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
// Note: This function uses code for sphere generation, but for stackCount = 2;
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
{
int stackCount = 2;
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
indexed_triangle_set mesh;
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j + 0.25 * M_PI;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return mesh;
}
indexed_triangle_set its_make_snap(double r, double h, float space_proportion, float bulge_proportion)
{
const float radius = (float)r;
const float height = (float)h;
const size_t sectors_cnt = 10; //(float)fa;
const float halfPI = 0.5f * (float)PI;
const float space_len = space_proportion * radius;
const float b_len = radius;
const float m_len = (1 + bulge_proportion) * radius;
const float t_len = 0.5f * radius;
const float b_height = 0.f;
const float m_height = 0.5f * height;
const float t_height = height;
const float b_angle = acos(space_len/b_len);
const float t_angle = acos(space_len/t_len);
const float b_angle_step = b_angle / (float)sectors_cnt;
const float t_angle_step = t_angle / (float)sectors_cnt;
const Vec2f b_vec = Eigen::Vector2f(0, b_len);
const Vec2f t_vec = Eigen::Vector2f(0, t_len);
auto add_side_vertices = [b_vec, t_vec, b_height, m_height, t_height](std::vector<stl_vertex>& vertices, float b_angle, float t_angle, const Vec2f& m_vec) {
Vec2f b_pt = Eigen::Rotation2Df(b_angle) * b_vec;
Vec2f m_pt = Eigen::Rotation2Df(b_angle) * m_vec;
Vec2f t_pt = Eigen::Rotation2Df(t_angle) * t_vec;
vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height));
vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height));
vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height));
};
auto add_side_facets = [](std::vector<stl_triangle_vertex_indices>& facets, int vertices_cnt, int frst_id, int scnd_id) {
int id = vertices_cnt - 1;
facets.emplace_back(frst_id, id - 2, id - 5);
facets.emplace_back(id - 2, id - 1, id - 5);
facets.emplace_back(id - 1, id - 4, id - 5);
facets.emplace_back(id - 4, id - 1, id);
facets.emplace_back(id, id - 3, id - 4);
facets.emplace_back(id, scnd_id, id - 3);
};
const float f = (b_len - m_len) / m_len; // Flattening
auto get_m_len = [b_len, f](float angle) {
const float rad_sqr = b_len * b_len;
const float sin_sqr = sin(angle) * sin(angle);
const float f_sqr = (1-f)*(1-f);
return sqrtf(rad_sqr / (1 + (1 / f_sqr - 1) * sin_sqr));
};
auto add_sub_mesh = [add_side_vertices, add_side_facets, get_m_len,
b_height, t_height, b_angle, t_angle, b_angle_step, t_angle_step]
(indexed_triangle_set& mesh, float center_x, float angle_rotation, int frst_vertex_id) {
auto& vertices = mesh.vertices;
auto& facets = mesh.indices;
// 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Vec3f(center_x, 0.f, b_height));
vertices.emplace_back(Vec3f(center_x, 0.f, t_height));
float b_angle_start = angle_rotation - b_angle;
float t_angle_start = angle_rotation - t_angle;
const float b_angle_stop = angle_rotation + b_angle;
const int frst_id = frst_vertex_id;
const int scnd_id = frst_id + 1;
// add first side vertices and internal facets
{
const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start));
add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec);
int id = (int)vertices.size() - 1;
facets.emplace_back(frst_id, id - 2, id - 1);
facets.emplace_back(frst_id, id - 1, id);
facets.emplace_back(frst_id, id, scnd_id);
}
// add d side vertices and facets
while (!is_approx(b_angle_start, b_angle_stop)) {
b_angle_start += b_angle_step;
t_angle_start += t_angle_step;
const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start));
add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec);
add_side_facets(facets, (int)vertices.size(), frst_id, scnd_id);
}
// add last internal facets to close the mesh
{
int id = (int)vertices.size() - 1;
facets.emplace_back(frst_id, scnd_id, id);
facets.emplace_back(frst_id, id, id - 1);
facets.emplace_back(frst_id, id - 1, id - 2);
}
};
indexed_triangle_set mesh;
mesh.vertices.reserve(2 * (3 * (2 * sectors_cnt + 1) + 2));
mesh.indices.reserve(2 * (6 * 2 * sectors_cnt + 6));
add_sub_mesh(mesh, -space_len, halfPI , 0);
add_sub_mesh(mesh, space_len, 3 * halfPI, (int)mesh.vertices.size());
return mesh;
}
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts) indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
{ {
std::vector<Vec3f> dst_vertices; std::vector<Vec3f> dst_vertices;

View file

@ -1,3 +1,14 @@
///|/ Copyright (c) Prusa Research 2017 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Filip Sykala @Jony01
///|/ Copyright (c) 2019 Sijmen Schoon
///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/TriangleMesh.pm:
///|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2012 - 2013 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_TriangleMesh_hpp_ #ifndef slic3r_TriangleMesh_hpp_
#define slic3r_TriangleMesh_hpp_ #define slic3r_TriangleMesh_hpp_
@ -337,9 +348,11 @@ indexed_triangle_set its_make_cube(double x, double y, double z);
indexed_triangle_set its_make_prism(float width, float length, float height); indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa); indexed_triangle_set its_make_sphere(double radius, double fa);
indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f);
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts); indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); } inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Lukáš Hejl @hejllukas
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
@ -2300,79 +2304,4 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
} }
} }
// BBS: implement plane cut with cgal
static Vec3d calc_plane_normal(const std::array<Vec3d, 4>& plane_points)
{
Vec3d v01 = plane_points[1] - plane_points[0];
Vec3d v12 = plane_points[2] - plane_points[1];
Vec3d plane_normal = v01.cross(v12);
plane_normal.normalize();
return plane_normal;
}
void cut_mesh
(
const indexed_triangle_set& mesh, // model object coordinate
std::array<Vec3d, 4> plane_points, // model object coordinate
indexed_triangle_set* upper,
indexed_triangle_set* lower,
bool triangulate_caps
)
{
assert(upper || lower);
if (upper == nullptr && lower == nullptr)
return;
BOOST_LOG_TRIVIAL(trace) << "cut_mesh - slicing object";
Vec3d plane_normal = calc_plane_normal(plane_points);
if (std::abs(plane_normal(0)) < EPSILON && std::abs(plane_normal(1)) < EPSILON) {
cut_mesh(mesh, plane_points[0](2), upper, lower);
return;
}
// BBS
if (std::abs(plane_normal(2)) < EPSILON) {
// keep the side on the normal direction
}
else if (plane_normal(2) < 0.0) {
std::reverse(plane_points.begin(), plane_points.end());
}
plane_normal = calc_plane_normal(plane_points);
Vec3d mid_point = { 0.0, 0.0, 0.0 };
for (auto pt : plane_points)
mid_point += pt;
mid_point /= (double)plane_points.size();
Vec3d movement = -mid_point;
Vec3d axis = { 0.0, 0.0, 0.0 };
double phi = 0.0;
Matrix3d matrix;
matrix.setIdentity();
Geometry::rotation_from_two_vectors(plane_normal, { 0.0, 0.0, 1.0 }, axis, phi, &matrix);
Vec3d angles = Geometry::extract_euler_angles(matrix);
movement = matrix * movement;
Transform3d transfo;
transfo.setIdentity();
transfo.translate(movement);
transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX()));
indexed_triangle_set mesh_temp = mesh;
its_transform(mesh_temp, transfo);
cut_mesh(mesh_temp, 0., upper, lower);
Transform3d transfo_inv = transfo.inverse();
if (upper) {
its_transform(*upper, transfo_inv);
}
if (lower) {
its_transform(*lower, transfo_inv);
}
}
} // namespace Slic3r } // namespace Slic3r

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2022 Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_TriangleMeshSlicer_hpp_ #ifndef slic3r_TriangleMeshSlicer_hpp_
#define slic3r_TriangleMeshSlicer_hpp_ #define slic3r_TriangleMeshSlicer_hpp_
@ -130,14 +134,6 @@ void cut_mesh(
indexed_triangle_set *lower, indexed_triangle_set *lower,
bool triangulate_caps = true); bool triangulate_caps = true);
// BBS } // namespace Slic3r
void cut_mesh(
const indexed_triangle_set &mesh,
std::array<Vec3d, 4> plane_points,
indexed_triangle_set *upper,
indexed_triangle_set *lower,
bool triangulate_caps = true);
}
#endif // slic3r_TriangleMeshSlicer_hpp_ #endif // slic3r_TriangleMeshSlicer_hpp_

View file

@ -160,6 +160,11 @@ void flush_logs();
// This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded. // This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded.
typedef std::string local_encoded_string; typedef std::string local_encoded_string;
// Returns next utf8 sequence length. =number of bytes in string, that creates together one utf-8 character.
// Starting at pos. ASCII characters returns 1. Works also if pos is in the middle of the sequence.
extern size_t get_utf8_sequence_length(const std::string& text, size_t pos = 0);
extern size_t get_utf8_sequence_length(const char *seq, size_t size);
// Convert an UTF-8 encoded string into local coding. // Convert an UTF-8 encoded string into local coding.
// On Windows, the UTF-8 string is converted to a local 8-bit code page. // On Windows, the UTF-8 string is converted to a local 8-bit code page.
// On OSX and Linux, this function does no conversion and returns a copy of the source string. // On OSX and Linux, this function does no conversion and returns a copy of the source string.

View file

@ -1,3 +1,9 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, David Kocík @kocikdav, Roman Beránek @zavorka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2021 Justin Schuh @jschuh
///|/ Copyright (c) Slic3r 2013 - 2015 Alessandro Ranellucci @alranel
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Utils.hpp" #include "Utils.hpp"
#include "I18N.hpp" #include "I18N.hpp"
@ -1030,6 +1036,76 @@ bool is_shapes_dir(const std::string& dir)
namespace Slic3r { namespace Slic3r {
size_t get_utf8_sequence_length(const std::string& text, size_t pos)
{
assert(pos < text.size());
return get_utf8_sequence_length(text.c_str() + pos, text.size() - pos);
}
size_t get_utf8_sequence_length(const char *seq, size_t size)
{
size_t length = 0;
unsigned char c = seq[0];
if (c < 0x80) { // 0x00-0x7F
// is ASCII letter
length++;
}
// Bytes 0x80 to 0xBD are trailer bytes in a multibyte sequence.
// pos is in the middle of a utf-8 sequence. Add the utf-8 trailer bytes.
else if (c < 0xC0) { // 0x80-0xBF
length++;
while (length < size) {
c = seq[length];
if (c < 0x80 || c >= 0xC0) {
break; // prevent overrun
}
length++; // add a utf-8 trailer byte
}
}
// Bytes 0xC0 to 0xFD are header bytes in a multibyte sequence.
// The number of one bits above the topmost zero bit indicates the number of bytes (including this one) in the whole sequence.
else if (c < 0xE0) { // 0xC0-0xDF
// add a utf-8 sequence (2 bytes)
if (2 > size) {
return size; // prevent overrun
}
length += 2;
}
else if (c < 0xF0) { // 0xE0-0xEF
// add a utf-8 sequence (3 bytes)
if (3 > size) {
return size; // prevent overrun
}
length += 3;
}
else if (c < 0xF8) { // 0xF0-0xF7
// add a utf-8 sequence (4 bytes)
if (4 > size) {
return size; // prevent overrun
}
length += 4;
}
else if (c < 0xFC) { // 0xF8-0xFB
// add a utf-8 sequence (5 bytes)
if (5 > size) {
return size; // prevent overrun
}
length += 5;
}
else if (c < 0xFE) { // 0xFC-0xFD
// add a utf-8 sequence (6 bytes)
if (6 > size) {
return size; // prevent overrun
}
length += 6;
}
else { // 0xFE-0xFF
// not a utf-8 sequence
length++;
}
return length;
}
// Encode an UTF-8 string to the local code page. // Encode an UTF-8 string to the local code page.
std::string encode_path(const char *src) std::string encode_path(const char *src)
{ {

View file

@ -132,8 +132,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoFdmSupports.hpp GUI/Gizmos/GLGizmoFdmSupports.hpp
GUI/Gizmos/GLGizmoFlatten.cpp GUI/Gizmos/GLGizmoFlatten.cpp
GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoFlatten.hpp
GUI/Gizmos/GLGizmoAdvancedCut.cpp GUI/Gizmos/GLGizmoCut.cpp
GUI/Gizmos/GLGizmoAdvancedCut.hpp GUI/Gizmos/GLGizmoCut.hpp
#GUI/Gizmos/GLGizmoHollow.cpp #GUI/Gizmos/GLGizmoHollow.cpp
#GUI/Gizmos/GLGizmoHollow.hpp #GUI/Gizmos/GLGizmoHollow.hpp
GUI/Gizmos/GLGizmoPainterBase.cpp GUI/Gizmos/GLGizmoPainterBase.cpp

View file

@ -411,6 +411,12 @@ private:
// plane coeffs for clipping in shaders // plane coeffs for clipping in shaders
std::array<double, 4> m_clipping_plane; std::array<double, 4> m_clipping_plane;
// plane coeffs for render volumes with different colors in shaders
// used by cut gizmo
std::array<double, 4> m_color_clip_plane;
bool m_use_color_clip_plane{ false };
std::array<ColorRGBA, 2> m_color_clip_plane_colors{ ColorRGBA::RED(), ColorRGBA::BLUE() };
struct Slope struct Slope
{ {
// toggle for slope rendering // toggle for slope rendering
@ -482,6 +488,14 @@ public:
const std::array<float, 2>& get_z_range() const { return m_z_range; } const std::array<float, 2>& get_z_range() const { return m_z_range; }
const std::array<double, 4>& get_clipping_plane() const { return m_clipping_plane; } const std::array<double, 4>& get_clipping_plane() const { return m_clipping_plane; }
void set_use_color_clip_plane(bool use) { m_use_color_clip_plane = use; }
void set_color_clip_plane(const Vec3d& cp_normal, double offset) {
for (int i = 0; i < 3; ++i)
m_color_clip_plane[i] = -cp_normal[i];
m_color_clip_plane[3] = offset;
}
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_color_clip_plane_colors = colors; }
bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; } bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; }
bool is_slope_active() const { return m_slope.active; } bool is_slope_active() const { return m_slope.active; }
void set_slope_active(bool active) { m_slope.active = active; } void set_slope_active(bool active) { m_slope.active = active; }

View file

@ -807,6 +807,10 @@ public:
bool get_use_clipping_planes() const { return m_use_clipping_planes; } bool get_use_clipping_planes() const { return m_use_clipping_planes; }
const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; }; const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
void set_use_color_clip_plane(bool use) { m_volumes.set_use_color_clip_plane(use); }
void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); }
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_volumes.set_color_clip_plane_colors(colors); }
void refresh_camera_scene_box(); void refresh_camera_scene_box();
BoundingBoxf3 volumes_bounding_box(bool current_plate_only = false) const; BoundingBoxf3 volumes_bounding_box(bool current_plate_only = false) const;

View file

@ -1,5 +1,10 @@
///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLSelectionRectangle.hpp" #include "GLSelectionRectangle.hpp"
#include "Camera.hpp" #include "Camera.hpp"
#include "CameraUtils.hpp"
#include "3DScene.hpp" #include "3DScene.hpp"
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
@ -29,35 +34,19 @@ namespace GUI {
m_end_corner = mouse_position; m_end_corner = mouse_position;
} }
std::vector<unsigned int> GLSelectionRectangle::stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points) std::vector<unsigned int> GLSelectionRectangle::contains(const std::vector<Vec3d>& points) const
{ {
std::vector<unsigned int> out; std::vector<unsigned int> out;
if (!is_dragging())
return out;
m_state = Off;
const Camera& camera = wxGetApp().plater()->get_camera();
Matrix4d modelview = camera.get_view_matrix().matrix();
Matrix4d projection= camera.get_projection_matrix().matrix();
Vec4i viewport(camera.get_viewport().data());
// Convert our std::vector to Eigen dynamic matrix.
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> pts(points.size(), 3);
for (size_t i=0; i<points.size(); ++i)
pts.block<1, 3>(i, 0) = points[i];
// Get the projections.
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> projections;
igl::project(pts, modelview, projection, viewport, projections);
// bounding box created from the rectangle corners - will take care of order of the corners // bounding box created from the rectangle corners - will take care of order of the corners
BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) }); const BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
// Iterate over all points and determine whether they're in the rectangle. // Iterate over all points and determine whether they're in the rectangle.
for (int i = 0; i<projections.rows(); ++i) const Camera &camera = wxGetApp().plater()->get_camera();
if (rectangle.contains(Point(projections(i, 0), canvas.get_canvas_size().get_height() - projections(i, 1)))) Points points_2d = CameraUtils::project(camera, points);
unsigned int size = static_cast<unsigned int>(points.size());
for (unsigned int i = 0; i< size; ++i)
if (rectangle.contains(points_2d[i]))
out.push_back(i); out.push_back(i);
return out; return out;

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLSelectionRectangle_hpp_ #ifndef slic3r_GLSelectionRectangle_hpp_
#define slic3r_GLSelectionRectangle_hpp_ #define slic3r_GLSelectionRectangle_hpp_
@ -25,8 +29,8 @@ public:
void dragging(const Vec2d& mouse_position); void dragging(const Vec2d& mouse_position);
// Given a vector of points in world coordinates, the function returns indices of those // Given a vector of points in world coordinates, the function returns indices of those
// that are in the rectangle. It then disables the rectangle. // that are in the rectangle.
std::vector<unsigned int> stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points); std::vector<unsigned int> contains(const std::vector<Vec3d>& points) const;
// Disables the rectangle. // Disables the rectangle.
void stop_dragging(); void stop_dragging();

View file

@ -2811,7 +2811,7 @@ void ObjectList::merge(bool to_multipart_object)
} }
} }
void ObjectList::merge_volumes() /*void ObjectList::merge_volumes()
{ {
std::vector<int> obj_idxs, vol_idxs; std::vector<int> obj_idxs, vol_idxs;
get_selection_indexes(obj_idxs, vol_idxs); get_selection_indexes(obj_idxs, vol_idxs);
@ -2839,11 +2839,11 @@ void ObjectList::merge_volumes()
else { else {
for (int vol_idx : vol_idxs) for (int vol_idx : vol_idxs)
selection.add_volume(last_obj_idx, vol_idx, 0, false); selection.add_volume(last_obj_idx, vol_idx, 0, false);
}*/ }#1#
#else #else
wxGetApp().plater()->merge(obj_idxs[0], vol_idxs); wxGetApp().plater()->merge(obj_idxs[0], vol_idxs);
#endif #endif
} }*/
void ObjectList::layers_editing() void ObjectList::layers_editing()
{ {

View file

@ -297,7 +297,7 @@ public:
void del_info_item(const int obj_idx, InfoItemType type); void del_info_item(const int obj_idx, InfoItemType type);
void split(); void split();
void merge(bool to_multipart_object); void merge(bool to_multipart_object);
void merge_volumes(); // BBS: merge parts to single part // void merge_volumes(); // BBS: merge parts to single part
void layers_editing(); void layers_editing();
void boolean(); // BBS: Boolean Operation of parts void boolean(); // BBS: Boolean Operation of parts

View file

@ -222,6 +222,9 @@ public:
void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); } void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); }
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); } void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }
virtual bool is_in_editing_mode() const { return false; }
virtual bool is_selection_rectangle_dragging() const { return false; }
protected: protected:
float last_input_window_width = 0; float last_input_window_width = 0;
virtual bool on_init() = 0; virtual bool on_init() = 0;

File diff suppressed because it is too large Load diff

View file

@ -1,72 +1,391 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoCut_hpp_ #ifndef slic3r_GLGizmoCut_hpp_
#define slic3r_GLGizmoCut_hpp_ #define slic3r_GLGizmoCut_hpp_
#include "GLGizmoBase.hpp" #include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLSelectionRectangle.hpp"
#include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "libslic3r/TriangleMesh.hpp" #include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/ObjectID.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/CutUtils.hpp"
#include "imgui/imgui.h"
namespace Slic3r { namespace Slic3r {
enum class CutConnectorType : int;
class ModelVolume;
class GLShaderProgram;
struct CutConnectorAttributes;
namespace GUI { namespace GUI {
class Selection;
class GLGizmoCut : public GLGizmoBase enum class SLAGizmoEventType : unsigned char;
namespace CommonGizmosDataObjects { class ObjectClipper; }
class GLGizmoCut3D : public GLGizmoBase
{ {
static const double Offset; enum GrabberID {
static const double Margin; X = 0,
Y,
double m_cut_z{ 0.0 }; Z,
double m_max_z{ 0.0 }; CutPlane,
double m_start_z{ 0.0 }; CutPlaneZRotation,
Vec3d m_drag_pos; CutPlaneXMove,
Vec3d m_drag_center; CutPlaneYMove,
bool m_keep_upper{ true }; Count,
bool m_keep_lower{ true };
bool m_rotate_lower{ false };
// BBS: m_do_segment
bool m_cut_to_parts {false};
bool m_do_segment{ false };
double m_segment_smoothing_alpha{ 0.5 };
int m_segment_number{ 5 };
struct CutContours
{
TriangleMesh mesh;
GLModel contours;
double cut_z{ 0.0 };
Vec3d position{ Vec3d::Zero() };
Vec3d shift{ Vec3d::Zero() };
ObjectID object_id;
int instance_idx{ -1 };
}; };
CutContours m_cut_contours; Transform3d m_rotation_m{ Transform3d::Identity() };
double m_snap_step{ 1.0 };
int m_connectors_group_id;
// archived values
Vec3d m_ar_plane_center { Vec3d::Zero() };
Transform3d m_start_dragging_m{ Transform3d::Identity() };
Vec3d m_plane_center{ Vec3d::Zero() };
// data to check position of the cut palne center on gizmo activation
Vec3d m_min_pos{ Vec3d::Zero() };
Vec3d m_max_pos{ Vec3d::Zero() };
Vec3d m_bb_center{ Vec3d::Zero() };
Vec3d m_center_offset{ Vec3d::Zero() };
BoundingBoxf3 m_bounding_box;
BoundingBoxf3 m_transformed_bounding_box;
// values from RotationGizmo
double m_radius{ 0.0 };
double m_grabber_radius{ 0.0 };
double m_grabber_connection_len{ 0.0 };
Vec3d m_cut_plane_start_move_pos {Vec3d::Zero()};
double m_snap_coarse_in_radius{ 0.0 };
double m_snap_coarse_out_radius{ 0.0 };
double m_snap_fine_in_radius{ 0.0 };
double m_snap_fine_out_radius{ 0.0 };
// dragging angel in hovered axes
double m_angle{ 0.0 };
TriangleMesh m_connector_mesh;
// workaround for using of the clipping plane normal
Vec3d m_clp_normal{ Vec3d::Ones() };
Vec3d m_line_beg{ Vec3d::Zero() };
Vec3d m_line_end{ Vec3d::Zero() };
Vec2d m_ldown_mouse_position{ Vec2d::Zero() };
GLModel m_grabber_connection;
GLModel m_cut_line;
PickingModel m_plane;
PickingModel m_sphere;
PickingModel m_cone;
PickingModel m_cube;
std::map<CutConnectorAttributes, PickingModel> m_shapes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_raycasters;
GLModel m_circle;
GLModel m_scale;
GLModel m_snap_radii;
GLModel m_reference_radius;
GLModel m_angle_arc;
Vec3d m_old_center;
Vec3d m_cut_normal;
struct InvalidConnectorsStatistics
{
unsigned int outside_cut_contour;
unsigned int outside_bb;
bool is_overlap;
void invalidate() {
outside_cut_contour = 0;
outside_bb = 0;
is_overlap = false;
}
} m_info_stats;
bool m_keep_upper{ true };
bool m_keep_lower{ true };
bool m_keep_as_parts{ false };
bool m_place_on_cut_upper{ true };
bool m_place_on_cut_lower{ false };
bool m_rotate_upper{ false };
bool m_rotate_lower{ false };
// Input params for cut with tongue and groove
Cut::Groove m_groove;
bool m_groove_editing { false };
bool m_is_slider_editing_done { false };
// Input params for cut with snaps
float m_snap_bulge_proportion{ 0.15f };
float m_snap_space_proportion{ 0.3f };
bool m_hide_cut_plane{ false };
bool m_connectors_editing{ false };
bool m_cut_plane_as_circle{ false };
float m_connector_depth_ratio{ 3.f };
float m_connector_size{ 2.5f };
float m_connector_angle{ 0.f };
float m_connector_depth_ratio_tolerance{ 0.1f };
float m_connector_size_tolerance{ 0.f };
float m_label_width{ 0.f };
float m_control_width{ 200.f };
bool m_imperial_units{ false };
float m_contour_width{ 0.4f };
float m_cut_plane_radius_koef{ 1.5f };
float m_shortcut_label_width{ -1.f };
mutable std::vector<bool> m_selected; // which pins are currently selected
int m_selected_count{ 0 };
GLSelectionRectangle m_selection_rectangle;
std::vector<size_t> m_invalid_connectors_idxs;
bool m_was_cut_plane_dragged { false };
bool m_was_contour_selected { false };
// Vertices of the groove used to detection if groove is valid
std::vector<Vec3d> m_groove_vertices;
class PartSelection {
public:
PartSelection() = default;
PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc);
PartSelection(const ModelObject* mo, int instance_idx_in);
~PartSelection() { m_model.clear_objects(); }
struct Part {
GLModel glmodel;
MeshRaycaster raycaster;
bool selected;
bool is_modifier;
};
void render(const Vec3d* normal, GLModel& sphere_model);
void toggle_selection(const Vec2d& mouse_pos);
void turn_over_selection();
ModelObject* model_object() { return m_model.objects.front(); }
bool valid() const { return m_valid; }
bool is_one_object() const;
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); }
std::vector<Cut::Part> get_cut_parts();
private:
Model m_model;
int m_instance_idx;
std::vector<Part> m_parts;
bool m_valid = false;
std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below
std::vector<size_t> m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object)
std::vector<Vec3d> m_contour_points; // Debugging
std::vector<std::vector<Vec3d>> m_debug_pts; // Debugging
void add_object(const ModelObject* object);
};
PartSelection m_part_selection;
bool m_show_shortcuts{ false };
std::vector<std::pair<wxString, wxString>> m_shortcuts;
enum class CutMode {
cutPlanar
, cutTongueAndGroove
//, cutGrig
//,cutRadial
//,cutModular
};
enum class CutConnectorMode {
Auto
, Manual
};
std::vector<std::string> m_modes;
size_t m_mode{ size_t(CutMode::cutPlanar) };
std::vector<std::string> m_connector_modes;
CutConnectorMode m_connector_mode{ CutConnectorMode::Manual };
std::vector<std::string> m_connector_types;
CutConnectorType m_connector_type;
std::vector<std::string> m_connector_styles;
int m_connector_style;
std::vector<std::string> m_connector_shapes;
int m_connector_shape_id;
std::vector<std::string> m_axis_names;
std::map<std::string, wxString> m_part_orientation_names;
std::map<std::string, std::string> m_labels_map;
public: public:
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
double get_cut_z() const { return m_cut_z; }
void set_cut_z(double cut_z);
std::string get_tooltip() const override; std::string get_tooltip() const override;
bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_contours = true);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool is_in_editing_mode() const override { return m_connectors_editing; }
bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); }
bool is_looking_forward() const;
/// <summary>
/// Drag of plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void shift_cut(double delta);
void rotate_vec3d_around_plane_center(Vec3d&vec);
void put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset);
void update_clipper();
void invalidate_cut_plane();
BoundingBoxf3 bounding_box() const;
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, const Transform3d& rotation_m = Transform3d::Identity()) const;
protected: protected:
virtual bool on_init() override; bool on_init() override;
virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } void on_load(cereal::BinaryInputArchive&ar) override;
virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } void on_save(cereal::BinaryOutputArchive&ar) const override;
virtual std::string on_get_name() const override; std::string on_get_name() const override;
virtual void on_set_state() override; void on_set_state() override;
virtual bool on_is_activable() const override; CommonGizmosDataID on_get_requirements() const override;
virtual void on_start_dragging() override; void on_set_hover_id() override;
virtual void on_update(const UpdateData& data) override; bool on_is_activable() const override;
virtual void on_render() override; bool on_is_selectable() const override;
virtual void on_render_for_picking() override; Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const;
virtual void on_render_input_window(float x, float y, float bottom_limit) override; void dragging_grabber_move(const GLGizmoBase::UpdateData &data);
void dragging_grabber_rotation(const GLGizmoBase::UpdateData &data);
void dragging_connector(const GLGizmoBase::UpdateData &data);
void on_dragging(const UpdateData&data) override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_render() override;
void render_debug_input_window(float x);
void adjust_window_position(float x, float y, float bottom_limit);
void unselect_all_connectors();
void select_all_connectors();
void render_shortcuts();
void apply_selected_connectors(std::function<void(size_t idx)> apply_fn);
void render_connectors_input_window(CutConnectors &connectors);
void render_build_size();
void reset_cut_plane();
void set_connectors_editing(bool connectors_editing);
void flip_cut_plane();
void process_contours();
void reset_cut_by_contours();
void render_flip_plane_button(bool disable_pred = false);
void add_vertical_scaled_interval(float interval);
void add_horizontal_scaled_interval(float interval);
void add_horizontal_shift(float shift);
void render_color_marker(float size, const ImU32& color);
void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance);
void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val);
bool render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val);
void render_snap_specific_input(const std::string& label, const wxString& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val);
void render_cut_plane_input_window(CutConnectors &connectors);
void init_input_window_data(CutConnectors &connectors);
void render_input_window_warning() const;
bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position);
bool delete_selected_connectors(CutConnectors&connectors);
void select_connector(int idx, bool select);
bool is_selection_changed(bool alt_down, bool shift_down);
void process_selection_rectangle(CutConnectors &connectors);
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
void update_raycasters_for_picking();
void set_volumes_picking_state(bool state);
void update_raycasters_for_picking_transform();
void update_plane_model();
void on_render_input_window(float x, float y, float bottom_limit) override;
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Cut gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Cut gizmo"); }
std::string get_action_snapshot_name() const override { return _u8L("Cut gizmo editing"); }
void data_changed(bool is_serializing) override;
Transform3d get_cut_matrix(const Selection& selection);
private: private:
void perform_cut(const Selection& selection); void set_center(const Vec3d&center, bool update_tbb = false);
double calc_projection(const Linef3& mouse_ray) const; void switch_to_mode(size_t new_mode);
BoundingBoxf3 bounding_box() const; bool render_cut_mode_combo();
void update_contours(); bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
void render_move_center_input(int axis);
void render_connect_mode_radio_button(CutConnectorMode mode);
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
bool render_connect_type_radio_button(CutConnectorType type);
bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
void render_connectors();
bool can_perform_cut() const;
bool has_valid_groove() const;
bool has_valid_contour() const;
void apply_connectors_in_model(ModelObject* mo, int &dowels_count);
bool cut_line_processing() const;
void discard_cut_line_processing();
void apply_color_clip_plane_colors();
void render_cut_plane();
static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
void render_rotation_snapping(GrabberID axis, const ColorRGBA& color);
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0);
void render_cut_plane_grabbers();
void render_cut_line();
void perform_cut(const Selection&selection);
void set_center_pos(const Vec3d&center_pos, bool update_tbb = false);
void update_bb();
void init_picking_models();
void init_rendering_items();
void render_clipper_cut();
void clear_selection();
void reset_connectors();
void init_connector_shapes();
void update_connector_shape();
void validate_connector_settings();
bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position);
void check_and_update_connectors_state();
void toggle_model_objects_visibility();
indexed_triangle_set its_make_groove_plane();
indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(ModelObject* mo, const std::string& connector_name);
}; };
} // namespace GUI } // namespace GUI

View file

@ -20,8 +20,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp"
//#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" //#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp"
// BBS #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp"
//#include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp" //#include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp"
//#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" //#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
@ -193,7 +192,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, m_is_dark ? "toolbar_rotate_dark.svg" : "toolbar_rotate.svg", EType::Rotate, &m_object_manipulation)); m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, m_is_dark ? "toolbar_rotate_dark.svg" : "toolbar_rotate.svg", EType::Rotate, &m_object_manipulation));
m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg", EType::Scale, &m_object_manipulation)); m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg", EType::Scale, &m_object_manipulation));
m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg", EType::Flatten)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg", EType::Flatten));
m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut)); m_gizmos.emplace_back(new GLGizmoCut3D(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut));
m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg", EType::MeshBoolean)); m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg", EType::MeshBoolean));
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg", EType::FdmSupports)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg", EType::FdmSupports));
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam));
@ -446,7 +445,7 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
else if (m_current == Measure) else if (m_current == Measure)
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Cut) else if (m_current == Cut)
return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoCut3D*>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MeshBoolean) else if (m_current == MeshBoolean)
return dynamic_cast<GLGizmoMeshBoolean*>(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoMeshBoolean*>(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else else
@ -861,13 +860,10 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
// m_parent.set_cursor(GLCanvas3D::Cross); // m_parent.set_cursor(GLCanvas3D::Cross);
processed = true; processed = true;
} }
else*/ if (m_current == Cut) else*/ if (m_current == Cut) {
{
// BBS
#if 0
auto do_move = [this, &processed](double delta_z) { auto do_move = [this, &processed](double delta_z) {
GLGizmoAdvancedCut* cut = dynamic_cast<GLGizmoAdvancedCut*>(get_current()); GLGizmoCut3D* cut = dynamic_cast<GLGizmoCut3D*>(get_current());
cut->set_cut_z(delta_z + cut->get_cut_z()); cut->shift_cut(delta_z);
processed = true; processed = true;
}; };
@ -875,10 +871,13 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
{ {
case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; } case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; }
case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; }
case WXK_SHIFT : case WXK_ALT: {
processed = get_current()->is_in_editing_mode();
}
default: { break; } default: { break; }
} }
#endif }
} else if (m_current == Simplify && keyCode == WXK_ESCAPE) { else if (m_current == Simplify && keyCode == WXK_ESCAPE) {
GLGizmoSimplify *simplify = dynamic_cast<GLGizmoSimplify *>(get_current()); GLGizmoSimplify *simplify = dynamic_cast<GLGizmoSimplify *>(get_current());
if (simplify != nullptr) if (simplify != nullptr)
processed = simplify->on_esc_key_down(); processed = simplify->on_esc_key_down();

View file

@ -511,10 +511,12 @@ void ImGuiWrapper::render()
m_new_frame_open = false; m_new_frame_open = false;
} }
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const ImVec2 ImGuiWrapper::calc_text_size(const wxString &text,
bool hide_text_after_double_hash,
float wrap_width) const
{ {
auto text_utf8 = into_u8(text); auto text_utf8 = into_u8(text);
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width); ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, hide_text_after_double_hash, wrap_width);
/*#ifdef __linux__ /*#ifdef __linux__
size.x *= m_style_scaling; size.x *= m_style_scaling;
@ -773,16 +775,30 @@ void ImGuiWrapper::end()
ImGui::End(); ImGui::End();
} }
bool ImGuiWrapper::button(const wxString &label) bool ImGuiWrapper::button(const wxString &label, const wxString& tooltip)
{ {
auto label_utf8 = into_u8(label); auto label_utf8 = into_u8(label);
return ImGui::Button(label_utf8.c_str()); const bool ret = ImGui::Button(label_utf8.c_str());
if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) {
auto tooltip_utf8 = into_u8(tooltip);
ImGui::SetTooltip(tooltip_utf8.c_str(), nullptr);
}
return ret;
} }
bool ImGuiWrapper::bbl_button(const wxString &label) bool ImGuiWrapper::bbl_button(const wxString &label, const wxString& tooltip)
{ {
auto label_utf8 = into_u8(label); auto label_utf8 = into_u8(label);
return ImGui::BBLButton(label_utf8.c_str()); const bool ret = ImGui::BBLButton(label_utf8.c_str());
if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) {
auto tooltip_utf8 = into_u8(tooltip);
ImGui::SetTooltip(tooltip_utf8.c_str(), nullptr);
}
return ret;
} }
bool ImGuiWrapper::button(const wxString& label, float width, float height) bool ImGuiWrapper::button(const wxString& label, float width, float height)
@ -791,6 +807,17 @@ bool ImGuiWrapper::button(const wxString& label, float width, float height)
return ImGui::Button(label_utf8.c_str(), ImVec2(width, height)); return ImGui::Button(label_utf8.c_str(), ImVec2(width, height));
} }
bool ImGuiWrapper::button(const wxString& label, const ImVec2 &size, bool enable)
{
disabled_begin(!enable);
auto label_utf8 = into_u8(label);
bool res = ImGui::Button(label_utf8.c_str(), size);
disabled_end();
return (enable) ? res : false;
}
bool ImGuiWrapper::radio_button(const wxString &label, bool active) bool ImGuiWrapper::radio_button(const wxString &label, bool active)
{ {
auto label_utf8 = into_u8(label); auto label_utf8 = into_u8(label);
@ -965,6 +992,8 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
m_last_slider_status.edited = ImGui::IsItemEdited(); m_last_slider_status.edited = ImGui::IsItemEdited();
m_last_slider_status.clicked = ImGui::IsItemClicked(); m_last_slider_status.clicked = ImGui::IsItemClicked();
m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit(); m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit();
if (!m_last_slider_status.can_take_snapshot)
m_last_slider_status.can_take_snapshot = ImGui::IsItemClicked();
if (!tooltip.empty() && ImGui::IsItemHovered()) if (!tooltip.empty() && ImGui::IsItemHovered())
this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width); this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width);
@ -1103,25 +1132,34 @@ bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip)
return res; return res;
} }
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection) bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/)
{
return combo(into_u8(label), options, selection, flags, label_width, item_width);
}
bool ImGuiWrapper::combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/)
{ {
// this is to force the label to the left of the widget: // this is to force the label to the left of the widget:
const bool hidden_label = boost::starts_with(label, "##");
if (!label.empty() && !hidden_label) {
text(label); text(label);
ImGui::SameLine(); ImGui::SameLine(label_width);
}
ImGui::PushItemWidth(item_width);
int selection_out = selection; int selection_out = selection;
bool res = false; bool res = false;
const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : ""; const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : "";
if (ImGui::BeginCombo("", selection_str)) { if (ImGui::BeginCombo(hidden_label ? label.c_str() : ("##" + label).c_str(), selection_str, flags)) {
for (int i = 0; i < (int)options.size(); i++) { for (int i = 0; i < (int)options.size(); i++) {
if (ImGui::Selectable(options[i].c_str(), i == selection)) { if (ImGui::Selectable(options[i].c_str(), i == selection)) {
selection_out = i; selection_out = i;
res = true;
} }
} }
ImGui::EndCombo(); ImGui::EndCombo();
res = true;
} }
selection = selection_out; selection = selection_out;

View file

@ -74,6 +74,11 @@ public:
bool edited { false }; bool edited { false };
bool clicked { false }; bool clicked { false };
bool deactivated_after_edit { false }; bool deactivated_after_edit { false };
// flag to indicate possibility to take snapshot from the slider value
// It's used from Gizmos to take snapshots just from the very beginning of the editing
bool can_take_snapshot { false };
// When Undo/Redo snapshot is taken, then call this function
void invalidate_snapshot() { can_take_snapshot = false; }
}; };
ImGuiWrapper(); ImGuiWrapper();
@ -95,12 +100,13 @@ public:
float scaled(float x) const { return x * m_font_size; } float scaled(float x) const { return x * m_font_size; }
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const; ImVec2 calc_text_size(const wxString &text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f) const;
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const; ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
ImVec2 get_item_spacing() const; ImVec2 get_item_spacing() const;
float get_slider_float_height() const; float get_slider_float_height() const;
const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; } const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; }
LastSliderStatus& get_last_slider_status() { return m_last_slider_status; }
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
void set_next_window_bg_alpha(float alpha); void set_next_window_bg_alpha(float alpha);
@ -118,9 +124,10 @@ public:
bool begin(const wxString& name, bool* close, int flags = 0); bool begin(const wxString& name, bool* close, int flags = 0);
void end(); void end();
bool button(const wxString &label); bool button(const wxString &label, const wxString& tooltip = {});
bool bbl_button(const wxString &label); bool bbl_button(const wxString &label, const wxString& tooltip = {});
bool button(const wxString& label, float width, float height); bool button(const wxString& label, float width, float height);
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
bool radio_button(const wxString &label, bool active); bool radio_button(const wxString &label, bool active);
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
@ -157,7 +164,9 @@ public:
bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0);
bool image_button(const wchar_t icon, const wxString& tooltip = L""); bool image_button(const wchar_t icon, const wxString& tooltip = L"");
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected // Use selection = -1 to not mark any option as selected
bool combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel); bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
Search::OptionViewParameters &view_params, Search::OptionViewParameters &view_params,

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "MsgDialog.hpp" #include "MsgDialog.hpp"
#include <wx/settings.h> #include <wx/settings.h>
@ -411,6 +415,58 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString&
finalize(); finalize();
} }
wxString get_wraped_wxString(const wxString& in, size_t line_len /*=80*/)
{
wxString out;
for (size_t i = 0; i < in.size();) {
// Overwrite the character (space or newline) starting at ibreak?
bool overwrite = false;
// UTF8 representation of wxString.
// Where to break the line, index of character at the start of a UTF-8 sequence.
size_t ibreak = size_t(-1);
// Overwrite the character at ibreak (it is a whitespace) or not?
size_t j = i;
for (size_t cnt = 0; j < in.size();) {
if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) {
// Overwrite the whitespace.
ibreak = j ++;
overwrite = true;
if (newline)
break;
} else if (in[j] == '/'
#ifdef _WIN32
|| in[j] == '\\'
#endif // _WIN32
) {
// Insert after the slash.
ibreak = ++ j;
overwrite = false;
} else
j += get_utf8_sequence_length(in.c_str() + j, in.size() - j);
if (++ cnt == line_len) {
if (ibreak == size_t(-1)) {
ibreak = j;
overwrite = false;
}
break;
}
}
if (j == in.size()) {
out.append(in.begin() + i, in.end());
break;
}
assert(ibreak != size_t(-1));
out.append(in.begin() + i, in.begin() + ibreak);
out.append('\n');
i = ibreak;
if (overwrite)
++ i;
}
return out;
}
// InfoDialog // InfoDialog
DownloadDialog::DownloadDialog(wxWindow *parent, const wxString &msg, const wxString &title, bool is_marked_msg /* = false*/, long style /* = wxOK | wxICON_INFORMATION*/) DownloadDialog::DownloadDialog(wxWindow *parent, const wxString &msg, const wxString &title, bool is_marked_msg /* = false*/, long style /* = wxOK | wxICON_INFORMATION*/)
: MsgDialog(parent, title, msg, style), msg(msg) : MsgDialog(parent, title, msg, style), msg(msg)

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2022 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_MsgDialog_hpp_ #ifndef slic3r_MsgDialog_hpp_
#define slic3r_MsgDialog_hpp_ #define slic3r_MsgDialog_hpp_
@ -128,6 +132,8 @@ public:
virtual ~WarningDialog() = default; virtual ~WarningDialog() = default;
}; };
wxString get_wraped_wxString(const wxString& text_in, size_t line_len = 80);
#if 1 #if 1
// Generic static line, used intead of wxStaticLine // Generic static line, used intead of wxStaticLine
//class StaticLine: public wxTextCtrl //class StaticLine: public wxTextCtrl

View file

@ -1,3 +1,22 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2022 Michael Kirsch
///|/ Copyright (c) 2021 Boleslaw Ciesielski
///|/ Copyright (c) 2019 John Drake @foxox
///|/
///|/ ported from lib/Slic3r/GUI/Plater.pm:
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) 2018 Martin Loidl @LoidlM
///|/ Copyright (c) 2017 Matthias Gazzari @qtux
///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens
///|/ Copyright (c) 2015 Daren Schwenke
///|/ Copyright (c) 2014 Mark Hindess
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
///|/ Copyright (c) 2012 Sam Wong
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Plater.hpp" #include "Plater.hpp"
#include "libslic3r/Config.hpp" #include "libslic3r/Config.hpp"
@ -128,6 +147,7 @@
#include "Gizmos/GLGizmosManager.hpp" #include "Gizmos/GLGizmosManager.hpp"
#endif // __APPLE__ #endif // __APPLE__
#include <libslic3r/CutUtils.hpp>
#include <wx/glcanvas.h> // Needs to be last because reasons :-/ #include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp" #include "WipeTowerDialog.hpp"
@ -8273,14 +8293,6 @@ void Plater::add_model(bool imperial_units, std::string fname)
wxGetApp().mainframe->update_title(); wxGetApp().mainframe->update_title();
} }
} }
std::array<Vec3d, 4> get_cut_plane(const BoundingBoxf3& bbox, const double& cut_height) {
std::array<Vec3d, 4> plane_pts;
plane_pts[0] = Vec3d(bbox.min(0), bbox.min(1), cut_height);
plane_pts[1] = Vec3d(bbox.max(0), bbox.min(1), cut_height);
plane_pts[2] = Vec3d(bbox.max(0), bbox.max(1), cut_height);
plane_pts[3] = Vec3d(bbox.min(0), bbox.max(1), cut_height);
return plane_pts;
}
void Plater::calib_pa(const Calib_Params& params) void Plater::calib_pa(const Calib_Params& params)
{ {
@ -8403,6 +8415,36 @@ void Plater::_calib_pa_pattern(const Calib_Params& params)
changed_objects({ 0 }); changed_objects({ 0 });
} }
void Plater::cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes)
{
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");
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
return;
wxBusyCursor wait;
const auto new_objects = Cut::cut_horizontal(object, instance_idx, z, attributes);
remove(obj_idx);
p->load_model_objects(new_objects);
// now process all updates of the 3d scene
update();
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
// which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call
for (size_t idx = 0; idx < p->model.objects.size(); idx++)
wxGetApp().obj_list()->update_info_items(idx);
Selection& selection = p->get_selection();
size_t last_id = p->model.objects.size() - 1;
for (size_t i = 0; i < new_objects.size(); ++i)
selection.add_object((unsigned int)(last_id - i), i == 0);
}
void Plater::_calib_pa_tower(const Calib_Params& params) { void Plater::_calib_pa_tower(const Calib_Params& params) {
add_model(false, Slic3r::resources_dir() + "/calib/pressure_advance/tower_with_seam.stl"); add_model(false, Slic3r::resources_dir() + "/calib/pressure_advance/tower_with_seam.stl");
@ -8439,8 +8481,7 @@ void Plater::_calib_pa_tower(const Calib_Params& params) {
auto new_height = std::ceil((params.end - params.start) / params.step) + 1; auto new_height = std::ceil((params.end - params.start) / params.step) + 1;
auto obj_bb = model().objects[0]->bounding_box(); auto obj_bb = model().objects[0]->bounding_box();
if (new_height < obj_bb.size().z()) { if (new_height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane(obj_bb, new_height); cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower);
cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
_calib_pa_select_added_objects(); _calib_pa_select_added_objects();
@ -8581,8 +8622,7 @@ void Plater::calib_temp(const Calib_Params& params) {
// add EPSILON offset to avoid cutting at the exact location where the flat surface is // add EPSILON offset to avoid cutting at the exact location where the flat surface is
auto new_height = block_count * 10.0 + EPSILON; auto new_height = block_count * 10.0 + EPSILON;
if (new_height < obj_bb.size().z()) { if (new_height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane(obj_bb, new_height); cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower);
cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
} }
@ -8592,8 +8632,7 @@ void Plater::calib_temp(const Calib_Params& params) {
if(block_count > 0){ if(block_count > 0){
auto new_height = block_count * 10.0 + EPSILON; auto new_height = block_count * 10.0 + EPSILON;
if (new_height < obj_bb.size().z()) { if (new_height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane(obj_bb, new_height); cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepUpper);
cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepUpper);
} }
} }
@ -8661,8 +8700,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
auto obj_bb = obj->bounding_box(); auto obj_bb = obj->bounding_box();
auto height = (params.end - params.start + 1) / params.step; auto height = (params.end - params.start + 1) / params.step;
if (height < obj_bb.size().z()) { if (height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane(obj_bb, height); cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
auto new_params = params; auto new_params = params;
@ -8710,8 +8748,7 @@ void Plater::calib_retraction(const Calib_Params& params)
auto obj_bb = obj->bounding_box(); auto obj_bb = obj->bounding_box();
auto height = 1.0 + 0.4 + ((params.end - params.start)) / params.step; auto height = 1.0 + 0.4 + ((params.end - params.start)) / params.step;
if (height < obj_bb.size().z()) { if (height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane(obj_bb, height); cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
p->background_process.fff_print()->set_calib_params(params); p->background_process.fff_print()->set_calib_params(params);
@ -8752,8 +8789,7 @@ void Plater::calib_VFA(const Calib_Params& params)
auto obj_bb = model().objects[0]->bounding_box(); auto obj_bb = model().objects[0]->bounding_box();
auto height = 5 * ((params.end - params.start) / params.step + 1); auto height = 5 * ((params.end - params.start) / params.step + 1);
if (height < obj_bb.size().z()) { if (height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane(obj_bb, height); cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
p->background_process.fff_print()->set_calib_params(params); p->background_process.fff_print()->set_calib_params(params);
@ -9858,27 +9894,16 @@ void Plater::convert_unit(ConversionType conv_type)
} }
} }
// BBS: replace z with plane_points void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& new_objects)
void Plater::cut(size_t obj_idx, size_t instance_idx, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes)
{ {
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); model().delete_object(obj_idx);
auto *object = p->model.objects[obj_idx]; sidebar().obj_list()->delete_object_from_list(obj_idx);
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); // suppress to call selection update for Object List to avoid call of early Gizmos on/off update
p->load_model_objects(new_objects, false, false);
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
return;
wxBusyCursor wait;
// BBS: replace z with plane_points
const auto new_objects = object->cut(instance_idx, plane_points, attributes);
remove(obj_idx);
p->load_model_objects(new_objects);
// now process all updates of the 3d scene // now process all updates of the 3d scene
update(); update();
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
// which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call
for (size_t idx = 0; idx < p->model.objects.size(); idx++) for (size_t idx = 0; idx < p->model.objects.size(); idx++)
@ -9888,62 +9913,10 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, std::array<Vec3d, 4> plane
size_t last_id = p->model.objects.size() - 1; size_t last_id = p->model.objects.size() - 1;
for (size_t i = 0; i < new_objects.size(); ++i) for (size_t i = 0; i < new_objects.size(); ++i)
selection.add_object((unsigned int)(last_id - i), i == 0); selection.add_object((unsigned int)(last_id - i), i == 0);
}
// BBS // UIThreadWorker w;
void Plater::segment(size_t obj_idx, size_t instance_idx, double smoothing_alpha, int segment_number) // arrange(w, true);
{ // w.wait_for_idle();
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");
Plater::TakeSnapshot snapshot(this, "Segment");
wxBusyCursor wait;
// real process
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology();
const size_t filament_cnt = print_tech != ptFFF ? 1 : preset_bundle.filament_presets.size();
const auto new_objects = object->segment(instance_idx, filament_cnt, smoothing_alpha, segment_number);
remove(obj_idx);
p->load_model_objects(new_objects);
Selection& selection = p->get_selection();
size_t last_id = p->model.objects.size() - 1;
for (size_t i = 0; i < new_objects.size(); ++i)
{
selection.add_object((unsigned int)(last_id - i), i == 0);
}
}
// BBS
void Plater::merge(size_t obj_idx, std::vector<int>& vol_indeces)
{
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
auto* object = p->model.objects[obj_idx];
Plater::TakeSnapshot snapshot(this, "Merge");
wxBusyCursor wait;
// real process
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology();
// BBS
const size_t filament_cnt = print_tech != ptFFF ? 1 : preset_bundle.filament_presets.size();
const auto new_objects = object->merge_volumes(vol_indeces);
remove(obj_idx);
p->load_model_objects(new_objects);
Selection& selection = p->get_selection();
size_t last_id = p->model.objects.size() - 1;
for (size_t i = 0; i < new_objects.size(); ++i)
{
selection.add_object((unsigned int)(last_id - i), i == 0);
}
} }
void Plater::export_gcode(bool prefer_removable) void Plater::export_gcode(bool prefer_removable)

View file

@ -1,3 +1,19 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/
///|/ ported from lib/Slic3r/GUI/Plater.pm:
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) 2018 Martin Loidl @LoidlM
///|/ Copyright (c) 2017 Matthias Gazzari @qtux
///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens
///|/ Copyright (c) 2015 Daren Schwenke
///|/ Copyright (c) 2014 Mark Hindess
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
///|/ Copyright (c) 2012 Sam Wong
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_Plater_hpp_ #ifndef slic3r_Plater_hpp_
#define slic3r_Plater_hpp_ #define slic3r_Plater_hpp_
@ -25,6 +41,7 @@
#include "libslic3r/PrintBase.hpp" #include "libslic3r/PrintBase.hpp"
#include "libslic3r/calib.hpp" #include "libslic3r/calib.hpp"
#include "libslic3r/CutUtils.hpp"
#define FILAMENT_SYSTEM_COLORS_NUM 16 #define FILAMENT_SYSTEM_COLORS_NUM 16
@ -41,8 +58,6 @@ class BuildVolume;
enum class BuildVolume_Type : unsigned char; enum class BuildVolume_Type : unsigned char;
class Model; class Model;
class ModelObject; class ModelObject;
enum class ModelObjectCutAttribute : int;
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
class ModelInstance; class ModelInstance;
class Print; class Print;
class SLAPrint; class SLAPrint;
@ -323,12 +338,7 @@ public:
void scale_selection_to_fit_print_volume(); void scale_selection_to_fit_print_volume();
void convert_unit(ConversionType conv_type); void convert_unit(ConversionType conv_type);
// BBS: replace z with plane_points void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
void cut(size_t obj_idx, size_t instance_idx, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes);
// BBS: segment model with CGAL
void segment(size_t obj_idx, size_t instance_idx, double smoothing_alpha=0.5, int segment_number=5);
void merge(size_t obj_idx, std::vector<int>& vol_indeces);
void send_to_printer(bool isall = false); void send_to_printer(bool isall = false);
void export_gcode(bool prefer_removable); void export_gcode(bool prefer_removable);
@ -742,6 +752,8 @@ private:
void _calib_pa_tower(const Calib_Params& params); void _calib_pa_tower(const Calib_Params& params);
void _calib_pa_select_added_objects(); void _calib_pa_select_added_objects();
void cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes);
friend class SuppressBackgroundProcessingUpdate; friend class SuppressBackgroundProcessingUpdate;
}; };

View file

@ -4,6 +4,7 @@
#include "../GUI/DeviceManager.hpp" #include "../GUI/DeviceManager.hpp"
#include "../GUI/Jobs/ProgressIndicator.hpp" #include "../GUI/Jobs/ProgressIndicator.hpp"
#include "../GUI/PartPlate.hpp" #include "../GUI/PartPlate.hpp"
#include "libslic3r/CutUtils.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
@ -133,7 +134,7 @@ bool CalibUtils::validate_input_flow_ratio(wxString flow_ratio, float* output_va
return true; return true;
} }
static void cut_model(Model &model, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes) static void cut_model(Model &model, double z, ModelObjectCutAttributes attributes)
{ {
size_t obj_idx = 0; size_t obj_idx = 0;
size_t instance_idx = 0; size_t instance_idx = 0;
@ -142,7 +143,7 @@ static void cut_model(Model &model, std::array<Vec3d, 4> plane_points, ModelObje
auto* object = model.objects[0]; auto* object = model.objects[0];
const auto new_objects = object->cut(instance_idx, plane_points, attributes); const auto new_objects = Cut::cut_horizontal(object, instance_idx, z, attributes);
model.delete_object(obj_idx); model.delete_object(obj_idx);
for (ModelObject *model_object : new_objects) { for (ModelObject *model_object : new_objects) {
@ -173,16 +174,6 @@ static void read_model_from_file(const std::string& input_file, Model& model)
object->ensure_on_bed(); object->ensure_on_bed();
} }
std::array<Vec3d, 4> get_cut_plane_points(const BoundingBoxf3 &bbox, const double &cut_height)
{
std::array<Vec3d, 4> plane_pts;
plane_pts[0] = Vec3d(bbox.min(0), bbox.min(1), cut_height);
plane_pts[1] = Vec3d(bbox.max(0), bbox.min(1), cut_height);
plane_pts[2] = Vec3d(bbox.max(0), bbox.max(1), cut_height);
plane_pts[3] = Vec3d(bbox.min(0), bbox.max(1), cut_height);
return plane_pts;
}
void CalibUtils::calib_PA(const X1CCalibInfos& calib_infos, int mode, wxString& error_message) void CalibUtils::calib_PA(const X1CCalibInfos& calib_infos, int mode, wxString& error_message)
{ {
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
@ -564,12 +555,7 @@ void CalibUtils::calib_temptue(const CalibInfo &calib_info, wxString &error_mess
// add EPSILON offset to avoid cutting at the exact location where the flat surface is // add EPSILON offset to avoid cutting at the exact location where the flat surface is
auto new_height = block_count * 10.0 + EPSILON; auto new_height = block_count * 10.0 + EPSILON;
if (new_height < obj_bb.size().z()) { if (new_height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts; cut_model(model, new_height, ModelObjectCutAttribute::KeepLower);
plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), new_height);
plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), new_height);
plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), new_height);
plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), new_height);
cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
} }
@ -579,12 +565,7 @@ void CalibUtils::calib_temptue(const CalibInfo &calib_info, wxString &error_mess
if (block_count > 0) { if (block_count > 0) {
auto new_height = block_count * 10.0 + EPSILON; auto new_height = block_count * 10.0 + EPSILON;
if (new_height < obj_bb.size().z()) { if (new_height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts; cut_model(model, new_height, ModelObjectCutAttribute::KeepUpper);
plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), new_height);
plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), new_height);
plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), new_height);
plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), new_height);
cut_model(model, plane_pts, ModelObjectCutAttribute::KeepUpper);
} }
} }
@ -670,12 +651,7 @@ void CalibUtils::calib_max_vol_speed(const CalibInfo &calib_info, wxString &erro
auto obj_bb = obj->bounding_box(); auto obj_bb = obj->bounding_box();
double height = (params.end - params.start + 1) / params.step; double height = (params.end - params.start + 1) / params.step;
if (height < obj_bb.size().z()) { if (height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts; cut_model(model, height, ModelObjectCutAttribute::KeepLower);
plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), height);
plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), height);
plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), height);
plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), height);
cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
auto new_params = params; auto new_params = params;
@ -731,12 +707,7 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message)
auto obj_bb = model.objects[0]->bounding_box(); auto obj_bb = model.objects[0]->bounding_box();
auto height = 5 * ((params.end - params.start) / params.step + 1); auto height = 5 * ((params.end - params.start) / params.step + 1);
if (height < obj_bb.size().z()) { if (height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts; cut_model(model, height, ModelObjectCutAttribute::KeepLower);
plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), height);
plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), height);
plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), height);
plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), height);
cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
else { else {
error_message = _L("The start, end or step is not valid value."); error_message = _L("The start, end or step is not valid value.");
@ -790,8 +761,7 @@ void CalibUtils::calib_retraction(const CalibInfo &calib_info, wxString &error_m
auto obj_bb = obj->bounding_box(); auto obj_bb = obj->bounding_box();
auto height = 1.0 + 0.4 + ((params.end - params.start)) / params.step; auto height = 1.0 + 0.4 + ((params.end - params.start)) / params.step;
if (height < obj_bb.size().z()) { if (height < obj_bb.size().z()) {
std::array<Vec3d, 4> plane_pts = get_cut_plane_points(obj_bb, height); cut_model(model, height, ModelObjectCutAttribute::KeepLower);
cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower);
} }
DynamicPrintConfig full_config; DynamicPrintConfig full_config;