Add Feature to disable snapping to buildplate (#11801)

* identified code for snapping to buidlplate

* rename internal name to ensure_on_bed to be consistent, saves option in 2mf, finish Move UI, use in both ensure_on_bed() functions

* makes auto_drop a per-object setting, removes global setting

* remove adUndef, add auto_drop to constructor/serialize

* fixes drop() button

* add "auto_drop" checkmark to "load as single object" dialog,
nothing changes if auto_drop == yes || "load as single object",
if auto_drop == false and "load as single object" == false the objects now retain their relative position to each other

* retains auto_drop (and printable) state when assembling or splitting objects,
adds ObjectList::printable_state_changed()  overload to be able to only provide ModelObject* vector

* adds dialog when splitting to ask if auto_drop should be disabled,
only shows when auto_drop enabled and atleast one volume  floating

* adds arrow indicator on bounding box if auto_drop == false

* removes unneeded code, keeps "auto_drop" naming consistent

* makes for loop simpler in set_printable, set_auto_drop and get_auto_drop,
makes get_auto_drop const,
fixes wording in Snapshot text

---------

Co-authored-by: Hanno Witzleb <hannowitzleb@gmail.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
This commit is contained in:
Hanno Witzleb
2026-04-15 22:36:25 +08:00
committed by GitHub
co-authored by Hanno Witzleb SoftFever Ian Bassi
parent f02aa7200e
commit f7ef8a14bd
15 changed files with 583 additions and 213 deletions
+14 -6
View File
@@ -313,6 +313,7 @@ static constexpr const char* TRANSFORM_ATTR = "transform";
// BBS // BBS
static constexpr const char* OFFSET_ATTR = "offset"; static constexpr const char* OFFSET_ATTR = "offset";
static constexpr const char* PRINTABLE_ATTR = "printable"; static constexpr const char* PRINTABLE_ATTR = "printable";
static constexpr const char* AUTO_DROP_ATTR = "auto_drop";
static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count"; static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count";
static constexpr const char* CUSTOM_SUPPORTS_ATTR = "paint_supports"; static constexpr const char* CUSTOM_SUPPORTS_ATTR = "paint_supports";
static constexpr const char* CUSTOM_FUZZY_SKIN_ATTR = "paint_fuzzy_skin"; static constexpr const char* CUSTOM_FUZZY_SKIN_ATTR = "paint_fuzzy_skin";
@@ -1204,7 +1205,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes); bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes);
bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes); bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes);
bool _create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter); bool _create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, const bool auto_drop, unsigned int recur_counter);
void _apply_transform(ModelInstance& instance, const Transform3d& transform); void _apply_transform(ModelInstance& instance, const Transform3d& transform);
@@ -3766,8 +3767,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR); std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR); int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
int auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1); return !m_load_model || _create_object_instance(path, object_id, transform, printable, auto_drop, 1);
} }
bool _BBS_3MF_Importer::_handle_end_item() bool _BBS_3MF_Importer::_handle_end_item()
@@ -3989,7 +3991,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true; return true;
} }
bool _BBS_3MF_Importer::_create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter) bool _BBS_3MF_Importer::_create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, const bool auto_drop, unsigned int recur_counter)
{ {
static const unsigned int MAX_RECURSIONS = 10; static const unsigned int MAX_RECURSIONS = 10;
@@ -4026,6 +4028,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false; return false;
} }
instance->printable = printable; instance->printable = printable;
instance->auto_drop = auto_drop;
m_instances.emplace_back(instance, transform); m_instances.emplace_back(instance, transform);
@@ -4058,6 +4061,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false; return false;
} }
instance->printable = printable; instance->printable = printable;
instance->auto_drop = auto_drop;
m_instances.emplace_back(instance, transform); m_instances.emplace_back(instance, transform);
} }
@@ -5614,12 +5618,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
unsigned int id; unsigned int id;
Transform3d transform; Transform3d transform;
bool printable; bool printable;
bool auto_drop;
BuildItem(std::string const & path, unsigned int id, const Transform3d& transform, const bool printable) BuildItem(std::string const & path, unsigned int id, const Transform3d& transform, const bool printable, const bool auto_drop)
: path(path) : path(path)
, id(id) , id(id)
, transform(transform) , transform(transform)
, printable(printable) , printable(printable)
, auto_drop(auto_drop)
{ {
} }
}; };
@@ -6784,7 +6790,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
Transform3d t = instance->get_matrix(); Transform3d t = instance->get_matrix();
// instance_id is just a 1 indexed index in build_items. // instance_id is just a 1 indexed index in build_items.
//assert(m_skip_static || curr_id == build_items.size() + 1); //assert(m_skip_static || curr_id == build_items.size() + 1);
build_items.emplace_back("", object_it->second.object_id, t, instance->printable);
build_items.emplace_back("", object_it->second.object_id, t, instance->printable, instance->auto_drop);
count++; count++;
} }
@@ -7220,7 +7227,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path); stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path);
stream << "\" " << TRANSFORM_ATTR << "=\""; stream << "\" " << TRANSFORM_ATTR << "=\"";
add_transformation(stream, item.transform); add_transformation(stream, item.transform);
stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n"; stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable;
stream << "\" " << AUTO_DROP_ATTR << "=\"" << item.auto_drop << "\"/>\n";
} }
stream << " </" << BUILD_TAG << ">\n"; stream << " </" << BUILD_TAG << ">\n";
+9 -3
View File
@@ -14,6 +14,7 @@
#include "Format/AMF.hpp" #include "Format/AMF.hpp"
#include "Format/svg.hpp" #include "Format/svg.hpp"
#include "Format/bbs_3mf.hpp"
#include "Format/DRC.hpp" #include "Format/DRC.hpp"
// BBS // BBS
#include "FaceDetector.hpp" #include "FaceDetector.hpp"
@@ -1088,7 +1089,6 @@ bool Model::is_fuzzy_skin_painted() const
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fuzzy_skin_painted(); }); return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fuzzy_skin_painted(); });
} }
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) 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()) if (mesh.empty())
@@ -1734,8 +1734,14 @@ void ModelObject::ensure_on_bed(bool allow_negative_z)
else else
z_offset = -this->min_z(); z_offset = -this->min_z();
if (z_offset != 0.0) if (z_offset != 0.0) {
translate_instances(z_offset * Vec3d::UnitZ()); for (size_t i = 0; i < instances.size(); ++i) {
if (!instances[i]->auto_drop)
continue;
translate_instance(i, z_offset * Vec3d::UnitZ());
}
}
} }
void ModelObject::translate_instances(const Vec3d& vector) void ModelObject::translate_instances(const Vec3d& vector)
+8 -2
View File
@@ -1251,6 +1251,7 @@ public:
ModelInstanceEPrintVolumeState print_volume_state; ModelInstanceEPrintVolumeState print_volume_state;
// Whether or not this instance is printable // Whether or not this instance is printable
bool printable; bool printable;
bool auto_drop;
bool use_loaded_id_for_label {false}; bool use_loaded_id_for_label {false};
int arrange_order = 0; // BBS int arrange_order = 0; // BBS
size_t loaded_id = 0; // BBS size_t loaded_id = 0; // BBS
@@ -1379,7 +1380,11 @@ private:
Polygon convex_hull; // BBS Polygon convex_hull; // BBS
// Constructor, which assigns a new unique ID. // Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject* object) : print_volume_state(ModelInstancePVS_Inside), printable(true), object(object), m_assemble_initialized(false) { assert(this->id().valid()); } explicit ModelInstance(ModelObject* object)
: print_volume_state(ModelInstancePVS_Inside), printable(true), auto_drop(true), object(object), m_assemble_initialized(false)
{
assert(this->id().valid());
}
// Constructor, which assigns a new unique ID. // Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) : explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation) m_transformation(other.m_transformation)
@@ -1387,6 +1392,7 @@ private:
, m_offset_to_assembly(other.m_offset_to_assembly) , m_offset_to_assembly(other.m_offset_to_assembly)
, print_volume_state(ModelInstancePVS_Inside) , print_volume_state(ModelInstancePVS_Inside)
, printable(other.printable) , printable(other.printable)
, auto_drop(other.auto_drop)
, object(object) , object(object)
, m_assemble_initialized(false) { assert(this->id().valid() && this->id() != other.id()); } , m_assemble_initialized(false) { assert(this->id().valid() && this->id() != other.id()); }
@@ -1400,7 +1406,7 @@ private:
ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); } ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); }
// BBS. Add added members to archive. // BBS. Add added members to archive.
template<class Archive> void serialize(Archive& ar) { template<class Archive> void serialize(Archive& ar) {
ar(m_transformation, print_volume_state, printable, m_assemble_transformation, m_offset_to_assembly, m_assemble_initialized); ar(m_transformation, print_volume_state, printable, auto_drop, m_assemble_transformation, m_offset_to_assembly, m_assemble_initialized);
} }
}; };
+115 -80
View File
@@ -2358,6 +2358,13 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx, bool allow_negative_z)
InstancesToZMap instances_min_z; InstancesToZMap instances_min_z;
for (GLVolume* volume : m_volumes.volumes) { for (GLVolume* volume : m_volumes.volumes) {
ModelObject* mo = m_model->objects[volume->object_idx()];
ModelInstance* mi = mo->instances[volume->instance_idx()];
if (!mi->auto_drop) {
continue;
}
if (volume->object_idx() == (int)object_idx && !volume->is_modifier) { if (volume->object_idx() == (int)object_idx && !volume->is_modifier) {
double min_z = volume->transformed_convex_hull_bounding_box().min.z(); double min_z = volume->transformed_convex_hull_bounding_box().min.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx()); std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
@@ -4867,28 +4874,30 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
// Move instances/volumes // Move instances/volumes
ModelObject* model_object = m_model->objects[object_idx]; ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) { if (model_object == nullptr)
if (selection_mode == Selection::Instance) { continue;
if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
if ((model_object->instances[instance_idx]->get_assemble_offset() - v->get_instance_offset()).norm() > 1e-2) {
model_object->instances[instance_idx]->set_assemble_transformation(v->get_instance_transformation());
}
} else {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
}
}
else if (selection_mode == Selection::Volume) {
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
object_moved = true; if (selection_mode == Selection::Instance) {
model_object->invalidate_bounding_box(); if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
if ((model_object->instances[instance_idx]->get_assemble_offset() - v->get_instance_offset()).norm() > 1e-2) {
model_object->instances[instance_idx]->set_assemble_transformation(v->get_instance_transformation());
}
} else {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
}
} }
else if (selection_mode == Selection::Volume) {
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
object_moved = true;
model_object->invalidate_bounding_box();
} }
else if (object_idx >= 1000 && object_idx < 1000 + n_plates) { else if (object_idx >= 1000 && object_idx < 1000 + n_plates) {
// Move a wipe tower proxy. // Move a wipe tower proxy.
@@ -4899,20 +4908,27 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(-1, 0); m_selection.notify_instance_update(-1, 0);
// Fixes flying instances // Fixes sinking/flying instances (snaps object to buildplate)
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* mo = m_model->objects[i.first];
const double shift_z = m->get_instance_min_z(i.second); ModelInstance* mi = mo->instances[i.second];
if (!mi->auto_drop) {
continue;
}
const double shift_z = mo->get_instance_min_z(i.second);
//BBS: don't call translate if the z is zero //BBS: don't call translate if the z is zero
if ((current_printer_technology() == ptSLA || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { if ((current_printer_technology() == ptSLA || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) {
const Vec3d shift(0.0, 0.0, -shift_z); const Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); mo->translate_instance(i.second, shift);
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(i.first, i.second); m_selection.notify_instance_update(i.first, i.second);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
} }
//BBS: nofity object list to update //BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items(); wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
@@ -4986,40 +5002,47 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
// Rotate instances/volumes. // Rotate instances/volumes.
ModelObject* model_object = m_model->objects[object_idx]; ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) { if (model_object == nullptr)
if (selection_mode == Selection::Instance) { continue;
if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
model_object->instances[instance_idx]->set_assemble_from_transform(v->get_instance_transformation().get_matrix()); if (selection_mode == Selection::Instance) {
} else { if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); model_object->instances[instance_idx]->set_assemble_from_transform(v->get_instance_transformation().get_matrix());
} } else {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
} }
else if (selection_mode == Selection::Volume) {
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
model_object->invalidate_bounding_box();
} }
else if (selection_mode == Selection::Volume) {
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
model_object->invalidate_bounding_box();
} }
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(-1, -1); m_selection.notify_instance_update(-1, -1);
if (m_canvas_type != CanvasAssembleView) { if (m_canvas_type != CanvasAssembleView) {
// Fixes sinking/flying instances // Fixes sinking/flying instances (snaps object to buildplate)
for (const std::pair<int, int> &i : done) { for (const std::pair<int, int> &i : done) {
ModelObject *m = m_model->objects[i.first]; ModelObject *mo = m_model->objects[i.first];
ModelInstance* mi = mo->instances[i.second];
if (!mi->auto_drop) {
continue;
}
// BBS: don't call translate if the z is zero // BBS: don't call translate if the z is zero
const double shift_z = m->get_instance_min_z(i.second); const double shift_z = mo->get_instance_min_z(i.second);
// leave sinking instances as sinking // leave sinking instances as sinking
if ((min_zs.find({i.first, i.second})->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { if ((min_zs.find({i.first, i.second})->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) {
const Vec3d shift(0.0, 0.0, -shift_z); const Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); mo->translate_instance(i.second, shift);
// BBS: notify instance updates to part plater list // BBS: notify instance updates to part plater list
m_selection.notify_instance_update(i.first, i.second); m_selection.notify_instance_update(i.first, i.second);
} }
@@ -5027,6 +5050,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
} }
} }
//BBS: nofity object list to update //BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items(); wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
@@ -5074,42 +5098,49 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
// Rotate instances/volumes // Rotate instances/volumes
ModelObject* model_object = m_model->objects[object_idx]; ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) { if (model_object == nullptr)
if (selection_mode == Selection::Instance) { continue;
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
} if (selection_mode == Selection::Instance) {
else if (selection_mode == Selection::Volume) { model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
model_object->invalidate_bounding_box();
} }
else if (selection_mode == Selection::Volume) {
auto cur_mv = model_object->volumes[volume_idx];
if (cur_mv->get_transformation() != v->get_volume_transformation()) {
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
cur_mv->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
model_object->invalidate_bounding_box();
} }
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(-1, -1); m_selection.notify_instance_update(-1, -1);
// Fixes sinking/flying instances // Fixes sinking/flying instances (snaps object to buildplate)
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* mo = m_model->objects[i.first];
ModelInstance* mi = mo->instances[i.second];
if (!mi->auto_drop) {
continue;
}
//BBS: don't call translate if the z is zero //BBS: don't call translate if the z is zero
double shift_z = m->get_instance_min_z(i.second); double shift_z = mo->get_instance_min_z(i.second);
// leave sinking instances as sinking // leave sinking instances as sinking
if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) {
Vec3d shift(0.0, 0.0, -shift_z); Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); mo->translate_instance(i.second, shift);
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(i.first, i.second); m_selection.notify_instance_update(i.first, i.second);
} }
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first)); wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
} }
//BBS: nofity object list to update //BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items(); wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
//BBS: notify object info update //BBS: notify object info update
@@ -5179,35 +5210,41 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
// Mirror instances/volumes // Mirror instances/volumes
ModelObject* model_object = m_model->objects[object_idx]; ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr) { if (model_object == nullptr)
if (selection_mode == Selection::Instance) continue;
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
else if (selection_mode == Selection::Volume) {
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
}
model_object->invalidate_bounding_box(); if (selection_mode == Selection::Instance)
model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation());
else if (selection_mode == Selection::Volume) {
if (model_object->volumes[volume_idx]->get_transformation() != v->get_volume_transformation()) {
model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation());
// BBS: backup
Slic3r::save_object_mesh(*model_object);
}
} }
model_object->invalidate_bounding_box();
} }
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(-1, -1); m_selection.notify_instance_update(-1, -1);
// Fixes sinking/flying instances // Fixes sinking/flying instances (snaps object to buildplate)
for (const std::pair<int, int>& i : done) { for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first]; ModelObject* mo = m_model->objects[i.first];
ModelInstance* mi = mo->instances[i.second];
if (!mi->auto_drop) {
continue;
}
//BBS: don't call translate if the z is zero //BBS: don't call translate if the z is zero
double shift_z = m->get_instance_min_z(i.second); double shift_z = mo->get_instance_min_z(i.second);
// leave sinking instances as sinking // leave sinking instances as sinking
if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) { if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) {
Vec3d shift(0.0, 0.0, -shift_z); Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift); m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift); mo->translate_instance(i.second, shift);
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
m_selection.notify_instance_update(i.first, i.second); m_selection.notify_instance_update(i.first, i.second);
} }
@@ -5216,10 +5253,8 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
//BBS: notify instance updates to part plater list //BBS: notify instance updates to part plater list
PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list(); PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list();
plate_list.notify_instance_update(i.first, i.second); plate_list.notify_instance_update(i.first, i.second);
//BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
} }
//BBS: nofity object list to update //BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items(); wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
+51 -1
View File
@@ -855,6 +855,23 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
return menu_item_printable; return menu_item_printable;
} }
wxMenuItem* MenuFactory::append_menu_item_auto_drop(wxMenu* menu)
{
wxString menu_text = _L("Auto Drop");
wxString menu_tooltip = _L("Automatically drops the selected object to the build plate");
wxMenuItem* menu_item_auto_drop = append_menu_check_item(
menu, wxID_ANY, menu_text, menu_tooltip,
[](wxCommandEvent&) { obj_list()->toggle_auto_drop(); }, menu);
m_parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) {
bool check = wxGetApp().plater()->get_selection().get_auto_drop();
evt.Check(check);
},
menu_item_auto_drop->GetId());
return menu_item_auto_drop;
}
void MenuFactory::append_menu_item_rename(wxMenu* menu) void MenuFactory::append_menu_item_rename(wxMenu* menu)
{ {
append_menu_item(menu, wxID_ANY, _L("Rename"), "", append_menu_item(menu, wxID_ANY, _L("Rename"), "",
@@ -1416,6 +1433,10 @@ void MenuFactory::create_extra_object_menu()
// Set filament insert menu item here // Set filament insert menu item here
// Set Printable // Set Printable
wxMenuItem* menu_item_printable = append_menu_item_printable(&m_object_menu); wxMenuItem* menu_item_printable = append_menu_item_printable(&m_object_menu);
m_object_menu.AppendSeparator();
wxMenuItem* menu_item_auto_drop = append_menu_item_auto_drop(&m_object_menu);
m_object_menu.AppendSeparator();
append_menu_item_per_object_process(&m_object_menu); append_menu_item_per_object_process(&m_object_menu);
// Enter per object parameters // Enter per object parameters
append_menu_item_per_object_settings(&m_object_menu); append_menu_item_per_object_settings(&m_object_menu);
@@ -1845,8 +1866,14 @@ wxMenu* MenuFactory::multi_selection_menu()
menu->AppendSeparator(); menu->AppendSeparator();
append_menu_item_set_printable(menu); append_menu_item_set_printable(menu);
menu->AppendSeparator();
append_menu_item_set_auto_drop(menu);
menu->AppendSeparator();
append_menu_item_per_object_process(menu); append_menu_item_per_object_process(menu);
menu->AppendSeparator(); menu->AppendSeparator();
append_menu_items_convert_unit(menu); append_menu_items_convert_unit(menu);
append_menu_item_replace_all_with_stl(menu); append_menu_item_replace_all_with_stl(menu);
//BBS //BBS
@@ -2032,7 +2059,7 @@ void MenuFactory::append_menu_item_drop(wxMenu* menu)
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false; return false;
else { else {
return (plater()->get_view3D_canvas3D()->get_selection().get_bounding_box().min.z() != 0); return (plater()->get_view3D_canvas3D()->get_selection().get_bounding_box().min.z() > SINKING_Z_THRESHOLD);
} //disable if model is on the bed / not in View3D } //disable if model is on the bed / not in View3D
}, m_parent); }, m_parent);
} }
@@ -2212,6 +2239,29 @@ void MenuFactory::append_menu_item_set_printable(wxMenu* menu)
}, menu_item_set_printable->GetId()); }, menu_item_set_printable->GetId());
} }
void MenuFactory::append_menu_item_set_auto_drop(wxMenu* menu)
{
const Selection& selection = plater()->canvas3D()->get_selection();
const bool current_auto_drop = selection.get_auto_drop();
wxString menu_text = _L("Auto Drop");
wxString menu_tooltip = _L("Automatically snaps the selected object to the build plate");
wxMenuItem* menu_item_set_auto_drop = append_menu_check_item(
menu, wxID_ANY, menu_text, menu_tooltip,
[this, current_auto_drop](wxCommandEvent&) {
Selection& selection = plater()->canvas3D()->get_selection();
selection.set_auto_drop(!current_auto_drop);
},
menu);
m_parent->Bind(
wxEVT_UPDATE_UI,
[current_auto_drop](wxUpdateUIEvent& evt) {
evt.Check(current_auto_drop);
plater()->set_current_canvas_as_dirty();
},
menu_item_set_auto_drop->GetId());
}
void MenuFactory::append_menu_item_locked(wxMenu* menu) void MenuFactory::append_menu_item_locked(wxMenu* menu)
{ {
const std::vector<wxString> names = { _L("Unlock"), _L("Lock") }; const std::vector<wxString> names = { _L("Unlock"), _L("Lock") };
+2
View File
@@ -137,6 +137,7 @@ private:
wxMenuItem* append_menu_item_change_type(wxMenu* menu); wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu); wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
wxMenuItem* append_menu_item_printable(wxMenu* menu); wxMenuItem* append_menu_item_printable(wxMenu* menu);
wxMenuItem* append_menu_item_auto_drop(wxMenu* menu);
void append_menu_item_rename(wxMenu* menu); void append_menu_item_rename(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
//wxMenuItem* append_menu_item_simplify(wxMenu* menu); //wxMenuItem* append_menu_item_simplify(wxMenu* menu);
@@ -171,6 +172,7 @@ private:
void append_menu_item_per_object_settings(wxMenu* menu); void append_menu_item_per_object_settings(wxMenu* menu);
void append_menu_item_change_filament(wxMenu* menu); void append_menu_item_change_filament(wxMenu* menu);
void append_menu_item_set_printable(wxMenu* menu); void append_menu_item_set_printable(wxMenu* menu);
void append_menu_item_set_auto_drop(wxMenu* menu);
void append_menu_item_locked(wxMenu* menu); void append_menu_item_locked(wxMenu* menu);
void append_menu_item_fill_bed(wxMenu *menu); void append_menu_item_fill_bed(wxMenu *menu);
void append_menu_item_plate_name(wxMenu *menu); void append_menu_item_plate_name(wxMenu *menu);
+80 -5
View File
@@ -914,15 +914,15 @@ void ObjectList::object_config_options_changed(const ObjectVolumeID& ov_id)
} }
} }
void ObjectList::printable_state_changed(const std::vector<ObjectVolumeID>& ov_ids) void ObjectList::printable_state_changed(const std::vector<ModelObject*> model_objects)
{ {
std::vector<size_t> obj_idxs; std::vector<size_t> obj_idxs;
for (const ObjectVolumeID ov_id : ov_ids) { for (const ModelObject* mo : model_objects) {
if (ov_id.object == nullptr) if (mo == nullptr)
continue; continue;
ModelInstance* mi = ov_id.object->instances[0]; ModelInstance* mi = mo->instances[0];
wxDataViewItem obj_item = m_objects_model->GetObjectItem(ov_id.object); wxDataViewItem obj_item = m_objects_model->GetObjectItem(mo);
m_objects_model->SetObjectPrintableState(mi->printable ? piPrintable : piUnprintable, obj_item); m_objects_model->SetObjectPrintableState(mi->printable ? piPrintable : piUnprintable, obj_item);
int obj_idx = m_objects_model->GetObjectIdByItem(obj_item); int obj_idx = m_objects_model->GetObjectIdByItem(obj_item);
@@ -939,6 +939,19 @@ void ObjectList::printable_state_changed(const std::vector<ObjectVolumeID>& ov_i
wxGetApp().plater()->update(); wxGetApp().plater()->update();
} }
void ObjectList::printable_state_changed(const std::vector<ObjectVolumeID>& ov_ids)
{
std::vector<ModelObject*> model_objects;
model_objects.reserve(ov_ids.size());
for (const ObjectVolumeID& ov_id : ov_ids) {
if (ov_id.object != nullptr)
model_objects.emplace_back(ov_id.object);
}
printable_state_changed(model_objects);
}
void ObjectList::assembly_plate_object_name() void ObjectList::assembly_plate_object_name()
{ {
m_objects_model->assembly_name(); m_objects_model->assembly_name();
@@ -1748,6 +1761,8 @@ void ObjectList::key_event(wxKeyEvent& event)
decrease_instances(); decrease_instances();
else if (event.GetUnicodeKey() == 'p') else if (event.GetUnicodeKey() == 'p')
toggle_printable_state(); toggle_printable_state();
else if (event.GetUnicodeKey() == 'd')
toggle_auto_drop();
else if (filaments_count() > 1) { else if (filaments_count() > 1) {
std::vector<wxChar> numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; std::vector<wxChar> numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
wxChar key_char = event.GetUnicodeKey(); wxChar key_char = event.GetUnicodeKey();
@@ -3044,6 +3059,17 @@ void ObjectList::merge(bool to_multipart_object)
volume->config.set_key_value("extruder", option->clone()); volume->config.set_key_value("extruder", option->clone());
} }
// merge printable and auto_drop values
// non-default have priority -> if one object has printable == false,
// then merged object will also have printable == false
if (object->instances[0]->printable == false) {
new_object->printable = false;
new_object->instances[0]->printable = false;
}
if (object->instances[0]->auto_drop == false) {
new_object->instances[0]->auto_drop = false;
}
// merge layers // merge layers
for (const auto& range : object->layer_config_ranges) for (const auto& range : object->layer_config_ranges)
new_object->layer_config_ranges.emplace(range); new_object->layer_config_ranges.emplace(range);
@@ -3077,6 +3103,9 @@ void ObjectList::merge(bool to_multipart_object)
// Add new object(merged) to the object_list // Add new object(merged) to the object_list
add_object_to_list(m_objects->size() - 1); add_object_to_list(m_objects->size() - 1);
if (new_object->printable == false) {
wxGetApp().obj_list()->printable_state_changed({new_object});
}
select_item(m_objects_model->GetItemById(m_objects->size() - 1)); select_item(m_objects_model->GetItemById(m_objects->size() - 1));
update_selections_on_canvas(); update_selections_on_canvas();
} }
@@ -6600,6 +6629,52 @@ void ObjectList::toggle_printable_state()
wxGetApp().plater()->reload_paint_after_background_process_apply(); wxGetApp().plater()->reload_paint_after_background_process_apply();
} }
void ObjectList::toggle_auto_drop()
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return;
for (auto item : sels) {
ItemType type = m_objects_model->GetItemType(item);
if (!(type & (itObject | itInstance)))
return;
}
const bool current_auto_drop = wxGetApp().plater()->get_selection().get_auto_drop();
take_snapshot("");
std::vector<size_t> obj_idxs;
for (auto item : sels) {
int obj_idx = m_objects_model->GetObjectIdByItem(item);
ModelObject* obj = object(obj_idx);
obj_idxs.emplace_back(static_cast<size_t>(obj_idx));
ItemType type = m_objects_model->GetItemType(item);
// set auto_drop value for selected instance/instances in object
if (type == itInstance) {
int inst_idx = m_objects_model->GetInstanceIdByItem(item);
obj->instances[inst_idx]->auto_drop = !current_auto_drop;
} else {
for (auto inst : obj->instances)
inst->auto_drop = !current_auto_drop;
if (current_auto_drop == false)
obj->ensure_on_bed();
}
}
sort(obj_idxs.begin(), obj_idxs.end());
obj_idxs.erase(unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end());
// update scene
wxGetApp().plater()->update();
wxGetApp().plater()->reload_paint_after_background_process_apply();
}
void ObjectList::enable_layers_editing() void ObjectList::enable_layers_editing()
{ {
wxDataViewItemArray sels; wxDataViewItemArray sels;
+2
View File
@@ -449,6 +449,7 @@ public:
//update printable state for item from objects model //update printable state for item from objects model
void update_printable_state(int obj_idx, int instance_idx); void update_printable_state(int obj_idx, int instance_idx);
void toggle_printable_state(); void toggle_printable_state();
void toggle_auto_drop();
void enable_layers_editing(); void enable_layers_editing();
//BBS: remove const qualifier //BBS: remove const qualifier
@@ -463,6 +464,7 @@ public:
void on_plate_selected(int plate_index); void on_plate_selected(int plate_index);
void notify_instance_updated(int obj_idx); void notify_instance_updated(int obj_idx);
void object_config_options_changed(const ObjectVolumeID& ov_id); void object_config_options_changed(const ObjectVolumeID& ov_id);
void printable_state_changed(const std::vector<ModelObject*> model_objects);
void printable_state_changed(const std::vector<ObjectVolumeID>& ov_ids); void printable_state_changed(const std::vector<ObjectVolumeID>& ov_ids);
// search objectlist // search objectlist
+1 -6
View File
@@ -1520,12 +1520,7 @@ void ObjectGridTable::update_value_to_object(Model* model, ObjectGridRow* grid_r
object->printable = grid_row->printable.value; object->printable = grid_row->printable.value;
object->instances[0]->printable = object->printable; object->instances[0]->printable = object->printable;
std::vector<ObjectVolumeID> object_volume_ids; wxGetApp().obj_list()->printable_state_changed({object});
ObjectVolumeID object_volume_id;
object_volume_id.object = object;
object_volume_id.volume = nullptr;
object_volume_ids.push_back(object_volume_id);
wxGetApp().obj_list()->printable_state_changed(object_volume_ids);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", change object %1%'s printable to %2%")%object->module_name %object->printable; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", change object %1%'s printable to %2%")%object->module_name %object->printable;
} }
} }
+2 -2
View File
@@ -205,8 +205,8 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
if (m_printable == printable) if (m_printable == printable)
return; return;
m_printable = printable; m_printable = printable;
m_printable_icon = m_printable == piUndef ? m_empty_bmp : m_printable_icon = m_printable == piUndef ?
create_scaled_bitmap(m_printable == piPrintable ? "check_on" : "check_off_focused"); m_empty_bmp : create_scaled_bitmap(m_printable == piPrintable ? "check_on" : "check_off_focused");
} }
void ObjectDataViewModelNode::set_variable_height_icon(VaryHeightIndicator vari_height) { void ObjectDataViewModelNode::set_variable_height_icon(VaryHeightIndicator vari_height) {
+5 -5
View File
@@ -241,7 +241,7 @@ public:
void SetExtruder(const wxString &extruder) { m_extruder = extruder; } void SetExtruder(const wxString &extruder) { m_extruder = extruder; }
void SetWarningIconName(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; } void SetWarningIconName(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; }
void SetLock(bool has_lock) { m_has_lock = has_lock; } void SetLock(bool has_lock) { m_has_lock = has_lock; }
const wxBitmap& GetBitmap() const { return m_bmp; } const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; } const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; } ItemType GetType() const { return m_type; }
InfoItemType GetInfoItemType() const { return m_info_item_type; } InfoItemType GetInfoItemType() const { return m_info_item_type; }
@@ -249,16 +249,16 @@ public:
int GetIdx() const { return m_idx; } int GetIdx() const { return m_idx; }
//BBS: add part plate related logic //BBS: add part plate related logic
void SetPlateIdx(const int& idx); void SetPlateIdx(const int& idx);
int GetPlateIdx() const { return m_plate_idx; } int GetPlateIdx() const { return m_plate_idx; }
ModelVolumeType GetVolumeType() { return m_volume_type; } ModelVolumeType GetVolumeType() { return m_volume_type; }
t_layer_height_range GetLayerRange() const { return m_layer_range; } t_layer_height_range GetLayerRange() const { return m_layer_range; }
wxString GetExtruder() { return m_extruder; } wxString GetExtruder() { return m_extruder; }
PrintIndicator IsPrintable() const { return m_printable; } PrintIndicator IsPrintable() const { return m_printable; }
VaryHeightIndicator IsVaribaleHeight() const { return m_variable_height; } VaryHeightIndicator IsVaribaleHeight() const { return m_variable_height; }
// BBS // BBS
bool HasColorPainting() const { return m_color_enable; } bool HasColorPainting() const { return m_color_enable; }
bool HasSupportPainting() const { return m_support_enable; } bool HasSupportPainting() const { return m_support_enable; }
bool HasSinking() const { return m_sink_enable; } bool HasSinking() const { return m_sink_enable; }
bool IsActionEnabled() const { return m_action_enable; } bool IsActionEnabled() const { return m_action_enable; }
void UpdateExtruderAndColorIcon(wxString extruder = ""); void UpdateExtruderAndColorIcon(wxString extruder = "");
+78 -16
View File
@@ -4507,7 +4507,7 @@ struct Plater::priv
// BBS: backup & restore // BBS: backup & restore
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, LoadStrategy strategy, bool ask_multi = false); std::vector<size_t> load_files(const std::vector<fs::path>& input_files, LoadStrategy strategy, bool ask_multi = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool split_object = false); std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool split_object = false, bool auto_drop = true);
fs::path get_export_file_path(GUI::FileType file_type); fs::path get_export_file_path(GUI::FileType file_type);
wxString get_export_file(GUI::FileType file_type); wxString get_export_file(GUI::FileType file_type);
@@ -4538,7 +4538,8 @@ struct Plater::priv
void center_selection(); void center_selection();
void drop_selection(); void drop_selection();
void mirror(Axis axis); void mirror(Axis axis);
void split_object(); void split_object(bool auto_drop = true);
void split_object(int obj_idx, bool auto_drop = true);
void split_volume(); void split_volume();
void scale_selection_to_fit_print_volume(); void scale_selection_to_fit_print_volume();
@@ -6715,14 +6716,37 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (new_model != nullptr && new_model->objects.size() > 1) { if (new_model != nullptr && new_model->objects.size() > 1) {
//BBS do not popup this dialog //BBS do not popup this dialog
bool new_model_auto_drop = true;
int single_object_answer = false;
if (ask_multi) { if (ask_multi) {
MessageDialog msg_dlg(q, _L("Load these files as a single object with multiple parts?\n"), _L("Object with multiple parts was detected"), RichMessageDialog dlg(q, _L("Load these files as a single object with multiple parts?\n"),
wxICON_WARNING | wxYES | wxNO); _L("Object with multiple parts was detected"), wxICON_QUESTION | wxYES_NO);
if (msg_dlg.ShowModal() == wxID_YES) { new_model->convert_multipart_object(filaments_cnt); }
dlg.ShowCheckBox(_L("Auto-Drop"), true);
single_object_answer = dlg.ShowModal();
if (dlg.IsCheckBoxChecked() == false)
new_model_auto_drop = false;
// convert to multipart and split after load_model_objects
// to keep relative positioning if auto_drop == false
if (single_object_answer == wxID_YES || new_model_auto_drop == false)
new_model->convert_multipart_object(filaments_cnt);
} }
auto loaded_idxs = load_model_objects(new_model->objects); // TODO
// DONE always convert to multipart, split afterwards to retain relative position
// DONE if !auto_drop move all objects over the z-position 0, so that none are clipped by the bed.
// DONE retain auto_drop (and printable) state when assembling or splitting objects.
// DONE when manually split to object ask users if looks_like_multipart and none have auto_drob disabled if they want to disable auto_drop for all resulting objects.
// - add icon in object list, similar to fuzzy painting, etc.
auto loaded_idxs = load_model_objects(new_model->objects, false, false, new_model_auto_drop);
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
if (single_object_answer == wxID_NO && new_model_auto_drop == false) {
split_object(loaded_idxs[0], new_model_auto_drop);
}
} }
if (load_config) { if (load_config) {
@@ -6889,7 +6913,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
#define AUTOPLACEMENT_ON_LOAD #define AUTOPLACEMENT_ON_LOAD
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool split_object) std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool split_object, bool auto_drop)
{ {
const Vec3d bed_size = Slic3r::to_3d(this->bed.build_volume().bounding_volume2d().size(), 1.0) - 2.0 * Vec3d::Ones(); const Vec3d bed_size = Slic3r::to_3d(this->bed.build_volume().bounding_volume2d().size(), 1.0) - 2.0 * Vec3d::Ones();
@@ -6916,7 +6940,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
#else /* AUTOPLACEMENT_ON_LOAD */ #else /* AUTOPLACEMENT_ON_LOAD */
// if object has no defined position(s) we need to rearrange everything after loading // if object has no defined position(s) we need to rearrange everything after loading
// need_arrange = true; // need_arrange = true;
// add a default instance and center object around origin // add a default instance and center object around origin
object->center_around_origin(); // also aligns object to Z = 0 object->center_around_origin(); // also aligns object to Z = 0
ModelInstance* instance = object->add_instance(); ModelInstance* instance = object->add_instance();
@@ -6955,7 +6979,20 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
} }
} }
object->ensure_on_bed(allow_negative_z); if (!auto_drop) {
for (size_t i = 0; i < object->instances.size(); ++i) {
ModelInstance* instance = object->instances[i];
instance->auto_drop = auto_drop;
}
// if under the bed, move over the bed
double dist_to_bed = std::min(object->min_z(), double(0));
object->translate_instances(Vec3d(0, 0, -dist_to_bed));
}
else {
object->ensure_on_bed(allow_negative_z);
}
if (!split_object) { if (!split_object) {
//BBS initial assemble transformation //BBS initial assemble transformation
for (ModelObject* model_object : model.objects) { for (ModelObject* model_object : model.objects) {
@@ -7484,9 +7521,16 @@ void Plater::find_new_position(const ModelInstancePtrs &instances)
m.apply(); m.apply();
} }
void Plater::priv::split_object() // split selected object into multiple objects by its volumes
void Plater::priv::split_object(bool auto_drop /* = true */)
{ {
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
priv::split_object(obj_idx, auto_drop);
}
// split provided object into multiple objects by its volumes
void Plater::priv::split_object(int obj_idx, bool auto_drop /* = true */)
{
if (obj_idx == -1) if (obj_idx == -1)
return; return;
@@ -7512,12 +7556,30 @@ void Plater::priv::split_object()
Plater::TakeSnapshot snapshot(q, "Split to Objects"); Plater::TakeSnapshot snapshot(q, "Split to Objects");
auto is_atleast_one_floating = [new_objects]() {
for (ModelObject* new_object : new_objects) {
if (new_object->get_instance_min_z(0) >= SINKING_MIN_Z_THRESHOLD)
return true;
}
return false;
};
bool split_auto_drop = auto_drop;
if (current_model_object->instances[0]->auto_drop && is_atleast_one_floating()) {
MessageDialog dlg(q, _L("Disable Auto-Drop to preserve z positioning?\n"),
_L("Object with floating parts was detected"), wxICON_QUESTION | wxYES_NO);
if (dlg.ShowModal() == wxID_YES)
split_auto_drop = false;
}
remove(obj_idx); remove(obj_idx);
// load all model objects at once, otherwise the plate would be rearranged after each one // load all model objects at once, otherwise the plate would be rearranged after each one
// causing original positions not to be kept // causing original positions not to be kept
//BBS: set split_object to true to avoid re-compute assemble matrix //BBS: set split_object to true to avoid re-compute assemble matrix
std::vector<size_t> idxs = load_model_objects(new_objects, false, true); std::vector<size_t> idxs = load_model_objects(new_objects, false, true, split_auto_drop);
wxGetApp().plater()->get_view3D_canvas3D()->update_instance_printable_state_for_objects(idxs);
// select newly added objects // select newly added objects
for (size_t idx : idxs) for (size_t idx : idxs)
@@ -16841,11 +16903,11 @@ void Plater::suppress_background_process(const bool stop_background_process)
this->p->suppressed_backround_processing_update = true; this->p->suppressed_backround_processing_update = true;
} }
void Plater::center_selection() { p->center_selection(); } void Plater::center_selection() { p->center_selection(); }
void Plater::drop_selection() { p->drop_selection(); } void Plater::drop_selection() { p->drop_selection(); }
void Plater::mirror(Axis axis) { p->mirror(axis); } void Plater::mirror(Axis axis) { p->mirror(axis); }
void Plater::split_object() { p->split_object(); } void Plater::split_object(bool auto_drop) { p->split_object(auto_drop); }
void Plater::split_volume() { p->split_volume(); } void Plater::split_volume() { p->split_volume(); }
void Plater::optimize_rotation() void Plater::optimize_rotation()
{ {
auto &w = get_ui_job_worker(); auto &w = get_ui_job_worker();
+2 -1
View File
@@ -643,7 +643,8 @@ public:
void drop_selection(); void drop_selection();
void search(bool plater_is_active, Preset::Type type, wxWindow *tag, TextInput *etag, wxWindow *stag); void search(bool plater_is_active, Preset::Type type, wxWindow *tag, TextInput *etag, wxWindow *stag);
void mirror(Axis axis); void mirror(Axis axis);
void split_object(); void split_object(bool auto_drop = true);
void split_object(int obj_idx, bool auto_drop = true);
void split_volume(); void split_volume();
void optimize_rotation(); void optimize_rotation();
// find all empty cells on the plate and won't overlap with exclusion areas // find all empty cells on the plate and won't overlap with exclusion areas
+203 -78
View File
@@ -525,8 +525,32 @@ void Selection::center()
void Selection::drop() void Selection::drop()
{ {
if (this->get_bounding_box().min.z() < SINKING_Z_THRESHOLD) {
return; // shouldnt happen, but better check anyways, already checked in append_menu_item_drop()
}
wxGetApp().plater()->take_snapshot(L("Move Object"));
this->move_to_center(Vec3d(0, 0, -this->get_bounding_box().min.z())); this->move_to_center(Vec3d(0, 0, -this->get_bounding_box().min.z()));
wxGetApp().plater()->get_view3D_canvas3D()->do_move(L("Move Object"));
for (unsigned int i : m_list) {
GLVolume& volume = *(*m_volumes)[i];
ModelObject* model_object = m_model->objects[volume.object_idx()];
if (model_object != nullptr) {
if (m_mode == Volume) {
ModelVolume* cur_mv = model_object->volumes[volume.volume_idx()];
cur_mv->set_transformation(volume.get_volume_transformation());
} else if (m_mode == Instance) {
model_object->instances[volume.instance_idx()]->set_transformation(volume.get_instance_transformation());
}
model_object->invalidate_bounding_box();
this->notify_instance_update(volume.object_idx(), volume.instance_idx());
}
}
wxGetApp().plater()->get_view3D_canvas3D()->post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
} }
void Selection::center_plate(const int plate_idx) { void Selection::center_plate(const int plate_idx) {
@@ -550,11 +574,9 @@ void Selection::set_printable(bool printable)
return; return;
std::set<std::pair<int, int>> instances_idxs; std::set<std::pair<int, int>> instances_idxs;
for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) for (const auto& [obj_idx, inst_list] : m_cache.content) {
{ for (int inst_idx : inst_list) {
for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) instances_idxs.insert({obj_idx, inst_idx});
{
instances_idxs.insert(std::make_pair(obj_it->first, *inst_it));
} }
} }
@@ -562,15 +584,66 @@ void Selection::set_printable(bool printable)
wxGetApp().plater()->take_snapshot(snapshot_text); wxGetApp().plater()->take_snapshot(snapshot_text);
// set printable value for all instances in object // set printable value for all instances in object
for (const std::pair<int, int>& i : instances_idxs) for (const auto& [obj_idx, inst_idx] : instances_idxs)
{ {
ModelObject* object = m_model->objects[i.first]; ModelObject* object = m_model->objects[obj_idx];
for (auto inst : object->instances) for (auto inst : object->instances)
inst->printable = printable; inst->printable = printable;
wxGetApp().obj_list()->update_printable_state(i.first, i.second); wxGetApp().obj_list()->update_printable_state(obj_idx, inst_idx);
//update printable state on canvas //update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)i.first); wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t) obj_idx);
}
// update scene
wxGetApp().plater()->update();
}
bool Selection::get_auto_drop() const {
if (!m_valid)
return true;
std::set<std::pair<int, int>> instances_idxs;
for (const auto& [obj_idx, inst_list] : m_cache.content) {
for (int inst_idx : inst_list) {
instances_idxs.insert({obj_idx, inst_idx});
}
}
// return false, if one of the instances in the selection has auto_drop disabled, otherwise return true
for (const auto& [obj_idx, inst_idx] : instances_idxs) {
ModelObject* object = m_model->objects[obj_idx];
for (const auto* inst : object->instances) {
if (!inst->auto_drop) {
return false;
}
}
}
return true;
}
void Selection::set_auto_drop(bool enabled)
{
if (!m_valid)
return;
std::set<std::pair<int, int>> instances_idxs;
for (const auto& [obj_idx, inst_list] : m_cache.content) {
for (int inst_idx : inst_list) {
instances_idxs.insert({obj_idx, inst_idx});
}
}
std::string snapshot_text = (boost::format("%1%") % (enabled ? "Set Selection Auto-Drop Enabled" : "Set Selection Auto-Drop Disabled")).str();
wxGetApp().plater()->take_snapshot(snapshot_text);
// set auto_drop value for all instances in object
for (const auto& [obj_idx, inst_idx] : instances_idxs) {
ModelObject* object = m_model->objects[obj_idx];
for (auto* inst : object->instances) {
inst->auto_drop = enabled;
}
} }
// update scene // update scene
@@ -1601,7 +1674,6 @@ void Selection::scale_and_translate(const Vec3d &scale, const Vec3d &world_trans
synchronize_unselected_volumes(); synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH #endif // !DISABLE_INSTANCES_SYNCH
ensure_on_bed();
set_bounding_boxes_dirty(); set_bounding_boxes_dirty();
if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) { if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) {
wxGetApp().plater()->canvas3D()->requires_check_outside_state(); wxGetApp().plater()->canvas3D()->requires_check_outside_state();
@@ -1612,7 +1684,6 @@ void Selection::mirror(Axis axis, TransformationType transformation_type)
{ {
const Vec3d mirror((axis == X) ? -1.0 : 1.0, (axis == Y) ? -1.0 : 1.0, (axis == Z) ? -1.0 : 1.0); const Vec3d mirror((axis == X) ? -1.0 : 1.0, (axis == Y) ? -1.0 : 1.0, (axis == Z) ? -1.0 : 1.0);
scale_and_translate(mirror, Vec3d::Zero(), transformation_type); scale_and_translate(mirror, Vec3d::Zero(), transformation_type);
} }
void Selection::translate(unsigned int object_idx, const Vec3d& displacement) void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
@@ -1927,7 +1998,8 @@ void Selection::render(float scale_factor)
m_scale_factor = scale_factor; m_scale_factor = scale_factor;
// render cumulative bounding box of selected volumes // render cumulative bounding box of selected volumes
const auto& [box, trafo] = get_bounding_box_in_current_reference_system(); const auto& [box, trafo] = get_bounding_box_in_current_reference_system();
render_bounding_box(box, trafo, const bool auto_drop = get_auto_drop();
render_bounding_box(box, trafo, auto_drop,
wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView ? ColorRGB::YELLOW(): ColorRGB::WHITE()); wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView ? ColorRGB::YELLOW(): ColorRGB::WHITE());
render_synchronized_volumes(); render_synchronized_volumes();
} }
@@ -2530,86 +2602,128 @@ void Selection::render_synchronized_volumes()
box = v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix()); box = v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix());
trafo = v.get_instance_transformation().get_matrix(); trafo = v.get_instance_transformation().get_matrix();
} }
render_bounding_box(box, trafo, ColorRGB::YELLOW()); const bool auto_drop = get_auto_drop();
render_bounding_box(box, trafo, auto_drop, ColorRGB::YELLOW());
} }
} }
} }
void Selection::render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const ColorRGB& color) void Selection::render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const bool auto_drop, const ColorRGB& color)
{ {
const BoundingBoxf3& curr_box = m_box.get_bounding_box(); const BoundingBoxf3& curr_box = m_box.get_bounding_box();
const bool curr_auto_drop = m_auto_drop;
if (!m_box.is_initialized() || !is_approx(box.min, curr_box.min) || !is_approx(box.max, curr_box.max)) { if (!m_box.is_initialized() || !is_approx(box.min, curr_box.min) || !is_approx(box.max, curr_box.max) || curr_auto_drop != auto_drop) {
m_box.reset(); m_box.reset();
m_auto_drop = auto_drop;
const Vec3f b_min = box.min.cast<float>(); const Vec3f b_min = box.min.cast<float>();
const Vec3f b_max = box.max.cast<float>(); const Vec3f b_max = box.max.cast<float>();
const Vec3f size = 0.2f * box.size().cast<float>(); const Vec3f size = 0.2f * box.size().cast<float>();
// 48 brackets + (24 if auto_drop == false)
int total_vertices = 48 + (auto_drop ? 0 : 24);
GLModel::Geometry init_data; GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
init_data.reserve_vertices(48); init_data.reserve_vertices(total_vertices);
init_data.reserve_indices(48); init_data.reserve_indices(total_vertices);
// vertices // vertices
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y() + size.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y() + size.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z() + size.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z() + size.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y() + size.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y() + size.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z() + size.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z() + size.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y() - size.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y() - size.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z() + size.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z() + size.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y() - size.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y() - size.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z() + size.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z() + size.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y() + size.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y() + size.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z() - size.z())); init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z() - size.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y() + size.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y() + size.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z() - size.z())); init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z() - size.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y() - size.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y() - size.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z() - size.z())); init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z() - size.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y() - size.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y() - size.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z()));
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z() - size.z())); init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z() - size.z()));
// indices // Indicate that auto_drop == false, by drawing an arrow on bottom corners
for (unsigned int i = 0; i < 48; ++i) { if (!auto_drop) {
float visual_scale = m_scale_factor;
float head_s = 2.0f * visual_scale; // Width of the arrowhead wings
float z_gap = 1.0f * visual_scale; // Gap between box and arrow tip
float arrow_h = 5.0f * visual_scale; // Total height of the arrow stem
// 4 bottom corners
std::vector<Vec3f> corners = {{b_min.x(), b_min.y(), b_min.z()},
{b_max.x(), b_min.y(), b_min.z()},
{b_max.x(), b_max.y(), b_min.z()},
{b_min.x(), b_max.y(), b_min.z()}};
for (const auto& pt : corners) {
Vec3f tip = {pt.x(), pt.y(), pt.z() - z_gap};
Vec3f base = {pt.x(), pt.y(), tip.z() - arrow_h};
// Stem
init_data.add_vertex(base);
init_data.add_vertex(tip);
// Wings
// Determine direction to the center of the footprint
float dirX = (pt.x() == b_min.x()) ? head_s : -head_s;
float dirY = (pt.y() == b_min.y()) ? head_s : -head_s;
// Wing 1 (X-Inward)
init_data.add_vertex(tip);
init_data.add_vertex(Vec3f(tip.x() + dirX, tip.y(), tip.z() - head_s));
// Wing 2 (Y-Inward)
init_data.add_vertex(tip);
init_data.add_vertex(Vec3f(tip.x(), tip.y() + dirY, tip.z() - head_s));
}
}
// Finalize indices
for (unsigned int i = 0; i < init_data.vertices_count(); ++i) {
init_data.add_index(i); init_data.add_index(i);
} }
@@ -2941,8 +3055,11 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
} }
else if (sync_rotation_type != SyncRotationType::NONE || mirrored) else if (sync_rotation_type != SyncRotationType::NONE || mirrored)
new_inst_trafo_j.linear() = (old_inst_trafo_j.linear() * old_inst_trafo_i.linear().inverse()) * curr_inst_trafo_i.linear(); new_inst_trafo_j.linear() = (old_inst_trafo_j.linear() * old_inst_trafo_i.linear().inverse()) * curr_inst_trafo_i.linear();
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
bool should_synchronize_z = m_model->objects[volume_j->object_idx()]->instances[volume_j->instance_idx()]->auto_drop == false;
if (should_synchronize_z && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
new_inst_trafo_j.translation().z() = curr_inst_trafo_i.translation().z(); new_inst_trafo_j.translation().z() = curr_inst_trafo_i.translation().z();
assert(is_rotation_xy_synchronized(curr_inst_trafo_i, new_inst_trafo_j)); assert(is_rotation_xy_synchronized(curr_inst_trafo_i, new_inst_trafo_j));
volume_j->set_instance_transformation(new_inst_trafo_j); volume_j->set_instance_transformation(new_inst_trafo_j);
done.insert(j); done.insert(j);
@@ -2985,17 +3102,25 @@ void Selection::ensure_on_bed()
InstancesToZMap instances_min_z; InstancesToZMap instances_min_z;
for (size_t i = 0; i < m_volumes->size(); ++i) { for (size_t i = 0; i < m_volumes->size(); ++i) {
GLVolume* volume = (*m_volumes)[i]; GLVolume* volume = (*m_volumes)[i];
if (!volume->is_wipe_tower && !volume->is_modifier && ModelObject* mo = m_model->objects[volume->object_idx()];
std::find(m_cache.sinking_volumes.begin(), m_cache.sinking_volumes.end(), i) == m_cache.sinking_volumes.end()) { ModelInstance* mi = mo->instances[volume->instance_idx()];
const double min_z = volume->transformed_convex_hull_bounding_box().min.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_min_z.find(instance);
if (it == instances_min_z.end())
it = instances_min_z.insert(InstancesToZMap::value_type(instance, DBL_MAX)).first;
it->second = std::min(it->second, min_z); if (mi->auto_drop == false
|| volume->is_wipe_tower
|| volume->is_modifier
|| std::find(m_cache.sinking_volumes.begin(), m_cache.sinking_volumes.end(), i) != m_cache.sinking_volumes.end())
{
continue;
} }
const double min_z = volume->transformed_convex_hull_bounding_box().min.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_min_z.find(instance);
if (it == instances_min_z.end())
it = instances_min_z.insert(InstancesToZMap::value_type(instance, DBL_MAX)).first;
it->second = std::min(it->second, min_z);
} }
for (GLVolume* volume : *m_volumes) { for (GLVolume* volume : *m_volumes) {
+5 -2
View File
@@ -181,6 +181,7 @@ private:
GLModel m_arrow; GLModel m_arrow;
GLModel m_curved_arrow; GLModel m_curved_arrow;
GLModel m_box; GLModel m_box;
bool m_auto_drop = true;
struct Planes struct Planes
{ {
std::array<Vec3f, 2> check_points{ Vec3f::Zero(), Vec3f::Zero() }; std::array<Vec3f, 2> check_points{ Vec3f::Zero(), Vec3f::Zero() };
@@ -237,6 +238,8 @@ public:
void drop(); void drop();
void center_plate(const int plate_idx); void center_plate(const int plate_idx);
void set_printable(bool printable); void set_printable(bool printable);
bool get_auto_drop() const;
void set_auto_drop(bool enabled);
void add_all(); void add_all();
void remove_all(); void remove_all();
@@ -370,7 +373,7 @@ public:
void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color, float scale) { void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color, float scale) {
m_scale_factor = scale; m_scale_factor = scale;
render_bounding_box(box, Transform3d::Identity(), color); render_bounding_box(box, Transform3d::Identity(), get_auto_drop(), color);
} }
//BBS //BBS
@@ -414,7 +417,7 @@ private:
m_bounding_sphere.reset(); m_bounding_sphere.reset();
} }
void render_synchronized_volumes(); void render_synchronized_volumes();
void render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const ColorRGB& color); void render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const bool auto_drop, const ColorRGB& color);
void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix); void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix);
void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix); void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix);
//BBS: GUI refactor: add uniform_scale from gizmo //BBS: GUI refactor: add uniform_scale from gizmo