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:
@@ -313,6 +313,7 @@ static constexpr const char* TRANSFORM_ATTR = "transform";
|
||||
// BBS
|
||||
static constexpr const char* OFFSET_ATTR = "offset";
|
||||
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* CUSTOM_SUPPORTS_ATTR = "paint_supports";
|
||||
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_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);
|
||||
|
||||
@@ -3766,8 +3767,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
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));
|
||||
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()
|
||||
@@ -3989,7 +3991,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
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;
|
||||
|
||||
@@ -4026,6 +4028,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return false;
|
||||
}
|
||||
instance->printable = printable;
|
||||
instance->auto_drop = auto_drop;
|
||||
|
||||
m_instances.emplace_back(instance, transform);
|
||||
|
||||
@@ -4058,6 +4061,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return false;
|
||||
}
|
||||
instance->printable = printable;
|
||||
instance->auto_drop = auto_drop;
|
||||
|
||||
m_instances.emplace_back(instance, transform);
|
||||
}
|
||||
@@ -5614,12 +5618,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
unsigned int id;
|
||||
Transform3d transform;
|
||||
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)
|
||||
, id(id)
|
||||
, transform(transform)
|
||||
, printable(printable)
|
||||
, auto_drop(auto_drop)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -6784,7 +6790,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
Transform3d t = instance->get_matrix();
|
||||
// instance_id is just a 1 indexed index in build_items.
|
||||
//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++;
|
||||
}
|
||||
|
||||
@@ -7220,7 +7227,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path);
|
||||
stream << "\" " << TRANSFORM_ATTR << "=\"";
|
||||
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";
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "Format/AMF.hpp"
|
||||
#include "Format/svg.hpp"
|
||||
#include "Format/bbs_3mf.hpp"
|
||||
#include "Format/DRC.hpp"
|
||||
// BBS
|
||||
#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(); });
|
||||
}
|
||||
|
||||
|
||||
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())
|
||||
@@ -1734,8 +1734,14 @@ void ModelObject::ensure_on_bed(bool allow_negative_z)
|
||||
else
|
||||
z_offset = -this->min_z();
|
||||
|
||||
if (z_offset != 0.0)
|
||||
translate_instances(z_offset * Vec3d::UnitZ());
|
||||
if (z_offset != 0.0) {
|
||||
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)
|
||||
|
||||
@@ -1251,6 +1251,7 @@ public:
|
||||
ModelInstanceEPrintVolumeState print_volume_state;
|
||||
// Whether or not this instance is printable
|
||||
bool printable;
|
||||
bool auto_drop;
|
||||
bool use_loaded_id_for_label {false};
|
||||
int arrange_order = 0; // BBS
|
||||
size_t loaded_id = 0; // BBS
|
||||
@@ -1379,7 +1380,11 @@ private:
|
||||
Polygon convex_hull; // BBS
|
||||
|
||||
// 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.
|
||||
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
|
||||
m_transformation(other.m_transformation)
|
||||
@@ -1387,6 +1392,7 @@ private:
|
||||
, m_offset_to_assembly(other.m_offset_to_assembly)
|
||||
, print_volume_state(ModelInstancePVS_Inside)
|
||||
, printable(other.printable)
|
||||
, auto_drop(other.auto_drop)
|
||||
, object(object)
|
||||
, 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()); }
|
||||
// BBS. Add added members to archive.
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2358,6 +2358,13 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx, bool allow_negative_z)
|
||||
InstancesToZMap instances_min_z;
|
||||
|
||||
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) {
|
||||
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());
|
||||
@@ -4867,28 +4874,30 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
||||
|
||||
// Move instances/volumes
|
||||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (model_object == nullptr)
|
||||
continue;
|
||||
|
||||
object_moved = true;
|
||||
model_object->invalidate_bounding_box();
|
||||
if (selection_mode == Selection::Instance) {
|
||||
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) {
|
||||
// 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
|
||||
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) {
|
||||
ModelObject* m = m_model->objects[i.first];
|
||||
const double shift_z = m->get_instance_min_z(i.second);
|
||||
ModelObject* mo = m_model->objects[i.first];
|
||||
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
|
||||
if ((current_printer_technology() == ptSLA || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) {
|
||||
const Vec3d shift(0.0, 0.0, -shift_z);
|
||||
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
|
||||
m_selection.notify_instance_update(i.first, i.second);
|
||||
}
|
||||
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
|
||||
}
|
||||
|
||||
//BBS: nofity object list to update
|
||||
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.
|
||||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance) {
|
||||
if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
|
||||
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());
|
||||
}
|
||||
if (model_object == nullptr)
|
||||
continue;
|
||||
|
||||
if (selection_mode == Selection::Instance) {
|
||||
if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) {
|
||||
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
|
||||
m_selection.notify_instance_update(-1, -1);
|
||||
|
||||
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) {
|
||||
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
|
||||
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
|
||||
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);
|
||||
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
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: nofity object list to update
|
||||
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
|
||||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
if (selection_mode == Selection::Instance) {
|
||||
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()) {
|
||||
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();
|
||||
if (model_object == nullptr)
|
||||
continue;
|
||||
|
||||
if (selection_mode == Selection::Instance) {
|
||||
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()) {
|
||||
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
|
||||
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) {
|
||||
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
|
||||
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
|
||||
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);
|
||||
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
|
||||
m_selection.notify_instance_update(i.first, i.second);
|
||||
}
|
||||
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
|
||||
}
|
||||
|
||||
//BBS: nofity object list to update
|
||||
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
||||
//BBS: notify object info update
|
||||
@@ -5179,35 +5210,41 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
||||
|
||||
// Mirror instances/volumes
|
||||
ModelObject* model_object = m_model->objects[object_idx];
|
||||
if (model_object != nullptr) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (model_object == nullptr)
|
||||
continue;
|
||||
|
||||
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
|
||||
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) {
|
||||
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
|
||||
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
|
||||
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);
|
||||
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
|
||||
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
|
||||
PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list();
|
||||
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
|
||||
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
||||
|
||||
|
||||
@@ -855,6 +855,23 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
|
||||
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)
|
||||
{
|
||||
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 Printable
|
||||
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);
|
||||
// Enter per object parameters
|
||||
append_menu_item_per_object_settings(&m_object_menu);
|
||||
@@ -1845,8 +1866,14 @@ wxMenu* MenuFactory::multi_selection_menu()
|
||||
menu->AppendSeparator();
|
||||
|
||||
append_menu_item_set_printable(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
append_menu_item_set_auto_drop(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
append_menu_item_per_object_process(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
append_menu_items_convert_unit(menu);
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
//BBS
|
||||
@@ -2032,7 +2059,7 @@ void MenuFactory::append_menu_item_drop(wxMenu* menu)
|
||||
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
|
||||
return false;
|
||||
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
|
||||
}, m_parent);
|
||||
}
|
||||
@@ -2184,7 +2211,7 @@ void MenuFactory::append_menu_item_change_filament(wxMenu* menu)
|
||||
void MenuFactory::append_menu_item_set_printable(wxMenu* menu)
|
||||
{
|
||||
const Selection& selection = plater()->canvas3D()->get_selection();
|
||||
bool all_printable = true;
|
||||
bool all_printable = true;
|
||||
ObjectList* list = obj_list();
|
||||
wxDataViewItemArray sels;
|
||||
list->GetSelections(sels);
|
||||
@@ -2212,6 +2239,29 @@ void MenuFactory::append_menu_item_set_printable(wxMenu* menu)
|
||||
}, 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)
|
||||
{
|
||||
const std::vector<wxString> names = { _L("Unlock"), _L("Lock") };
|
||||
|
||||
@@ -137,6 +137,7 @@ private:
|
||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_auto_drop(wxMenu* menu);
|
||||
void append_menu_item_rename(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_fix_through_netfabb(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_change_filament(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_fill_bed(wxMenu *menu);
|
||||
void append_menu_item_plate_name(wxMenu *menu);
|
||||
|
||||
@@ -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;
|
||||
for (const ObjectVolumeID ov_id : ov_ids) {
|
||||
if (ov_id.object == nullptr)
|
||||
for (const ModelObject* mo : model_objects) {
|
||||
if (mo == nullptr)
|
||||
continue;
|
||||
|
||||
ModelInstance* mi = ov_id.object->instances[0];
|
||||
wxDataViewItem obj_item = m_objects_model->GetObjectItem(ov_id.object);
|
||||
ModelInstance* mi = mo->instances[0];
|
||||
wxDataViewItem obj_item = m_objects_model->GetObjectItem(mo);
|
||||
m_objects_model->SetObjectPrintableState(mi->printable ? piPrintable : piUnprintable, 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();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
m_objects_model->assembly_name();
|
||||
@@ -1748,6 +1761,8 @@ void ObjectList::key_event(wxKeyEvent& event)
|
||||
decrease_instances();
|
||||
else if (event.GetUnicodeKey() == 'p')
|
||||
toggle_printable_state();
|
||||
else if (event.GetUnicodeKey() == 'd')
|
||||
toggle_auto_drop();
|
||||
else if (filaments_count() > 1) {
|
||||
std::vector<wxChar> numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
wxChar key_char = event.GetUnicodeKey();
|
||||
@@ -3044,6 +3059,17 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
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
|
||||
for (const auto& range : object->layer_config_ranges)
|
||||
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_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));
|
||||
update_selections_on_canvas();
|
||||
}
|
||||
@@ -6600,6 +6629,52 @@ void ObjectList::toggle_printable_state()
|
||||
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()
|
||||
{
|
||||
wxDataViewItemArray sels;
|
||||
|
||||
@@ -449,6 +449,7 @@ public:
|
||||
//update printable state for item from objects model
|
||||
void update_printable_state(int obj_idx, int instance_idx);
|
||||
void toggle_printable_state();
|
||||
void toggle_auto_drop();
|
||||
void enable_layers_editing();
|
||||
|
||||
//BBS: remove const qualifier
|
||||
@@ -463,6 +464,7 @@ public:
|
||||
void on_plate_selected(int plate_index);
|
||||
void notify_instance_updated(int obj_idx);
|
||||
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);
|
||||
|
||||
// search objectlist
|
||||
|
||||
@@ -1520,12 +1520,7 @@ void ObjectGridTable::update_value_to_object(Model* model, ObjectGridRow* grid_r
|
||||
object->printable = grid_row->printable.value;
|
||||
object->instances[0]->printable = object->printable;
|
||||
|
||||
std::vector<ObjectVolumeID> object_volume_ids;
|
||||
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);
|
||||
wxGetApp().obj_list()->printable_state_changed({object});
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", change object %1%'s printable to %2%")%object->module_name %object->printable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +205,8 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
|
||||
if (m_printable == printable)
|
||||
return;
|
||||
m_printable = printable;
|
||||
m_printable_icon = m_printable == piUndef ? m_empty_bmp :
|
||||
create_scaled_bitmap(m_printable == piPrintable ? "check_on" : "check_off_focused");
|
||||
m_printable_icon = m_printable == piUndef ?
|
||||
m_empty_bmp : create_scaled_bitmap(m_printable == piPrintable ? "check_on" : "check_off_focused");
|
||||
}
|
||||
|
||||
void ObjectDataViewModelNode::set_variable_height_icon(VaryHeightIndicator vari_height) {
|
||||
|
||||
@@ -241,7 +241,7 @@ public:
|
||||
void SetExtruder(const wxString &extruder) { m_extruder = extruder; }
|
||||
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; }
|
||||
const wxBitmap& GetBitmap() const { return m_bmp; }
|
||||
const wxBitmap& GetBitmap() const { return m_bmp; }
|
||||
const wxString& GetName() const { return m_name; }
|
||||
ItemType GetType() const { return m_type; }
|
||||
InfoItemType GetInfoItemType() const { return m_info_item_type; }
|
||||
@@ -249,16 +249,16 @@ public:
|
||||
int GetIdx() const { return m_idx; }
|
||||
//BBS: add part plate related logic
|
||||
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; }
|
||||
t_layer_height_range GetLayerRange() const { return m_layer_range; }
|
||||
wxString GetExtruder() { return m_extruder; }
|
||||
PrintIndicator IsPrintable() const { return m_printable; }
|
||||
VaryHeightIndicator IsVaribaleHeight() const { return m_variable_height; }
|
||||
VaryHeightIndicator IsVaribaleHeight() const { return m_variable_height; }
|
||||
// BBS
|
||||
bool HasColorPainting() const { return m_color_enable; }
|
||||
bool HasSupportPainting() const { return m_support_enable; }
|
||||
bool HasSinking() const { return m_sink_enable; }
|
||||
bool HasSupportPainting() const { return m_support_enable; }
|
||||
bool HasSinking() const { return m_sink_enable; }
|
||||
bool IsActionEnabled() const { return m_action_enable; }
|
||||
void UpdateExtruderAndColorIcon(wxString extruder = "");
|
||||
|
||||
|
||||
@@ -4507,7 +4507,7 @@ struct Plater::priv
|
||||
|
||||
// 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_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);
|
||||
wxString get_export_file(GUI::FileType file_type);
|
||||
@@ -4538,7 +4538,8 @@ struct Plater::priv
|
||||
void center_selection();
|
||||
void drop_selection();
|
||||
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 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) {
|
||||
//BBS do not popup this dialog
|
||||
|
||||
bool new_model_auto_drop = true;
|
||||
int single_object_answer = false;
|
||||
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"),
|
||||
wxICON_WARNING | wxYES | wxNO);
|
||||
if (msg_dlg.ShowModal() == wxID_YES) { new_model->convert_multipart_object(filaments_cnt); }
|
||||
RichMessageDialog dlg(q, _L("Load these files as a single object with multiple parts?\n"),
|
||||
_L("Object with multiple parts was detected"), wxICON_QUESTION | wxYES_NO);
|
||||
|
||||
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());
|
||||
|
||||
if (single_object_answer == wxID_NO && new_model_auto_drop == false) {
|
||||
split_object(loaded_idxs[0], new_model_auto_drop);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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();
|
||||
|
||||
@@ -6916,7 +6940,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
||||
#else /* AUTOPLACEMENT_ON_LOAD */
|
||||
// if object has no defined position(s) we need to rearrange everything after loading
|
||||
// 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
|
||||
ModelInstance* instance = object->add_instance();
|
||||
|
||||
@@ -6925,7 +6949,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
||||
instance->set_offset(Slic3r::to_3d(this->bed.build_volume().bed_center(), -object->origin_translation(2)));
|
||||
#endif /* AUTOPLACEMENT_ON_LOAD */
|
||||
}
|
||||
|
||||
|
||||
//BBS: when the object is too large, let the user choose whether to scale it down
|
||||
for (size_t i = 0; i < object->instances.size(); ++i) {
|
||||
ModelInstance* instance = object->instances[i];
|
||||
@@ -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) {
|
||||
//BBS initial assemble transformation
|
||||
for (ModelObject* model_object : model.objects) {
|
||||
@@ -6965,7 +7002,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
||||
model_object->instances[i]->set_assemble_transformation(model_object->instances[i]->get_transformation());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7032,7 +7069,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
||||
// which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call
|
||||
for (const size_t idx : obj_idxs)
|
||||
wxGetApp().obj_list()->update_info_items(idx);
|
||||
|
||||
|
||||
object_list_changed();
|
||||
|
||||
this->schedule_background_process();
|
||||
@@ -7484,9 +7521,16 @@ void Plater::find_new_position(const ModelInstancePtrs &instances)
|
||||
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();
|
||||
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)
|
||||
return;
|
||||
|
||||
@@ -7512,12 +7556,30 @@ void Plater::priv::split_object()
|
||||
|
||||
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);
|
||||
|
||||
// load all model objects at once, otherwise the plate would be rearranged after each one
|
||||
// causing original positions not to be kept
|
||||
//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
|
||||
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;
|
||||
}
|
||||
|
||||
void Plater::center_selection() { p->center_selection(); }
|
||||
void Plater::drop_selection() { p->drop_selection(); }
|
||||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||
void Plater::split_object() { p->split_object(); }
|
||||
void Plater::split_volume() { p->split_volume(); }
|
||||
void Plater::center_selection() { p->center_selection(); }
|
||||
void Plater::drop_selection() { p->drop_selection(); }
|
||||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||
void Plater::split_object(bool auto_drop) { p->split_object(auto_drop); }
|
||||
void Plater::split_volume() { p->split_volume(); }
|
||||
void Plater::optimize_rotation()
|
||||
{
|
||||
auto &w = get_ui_job_worker();
|
||||
|
||||
@@ -643,7 +643,8 @@ public:
|
||||
void drop_selection();
|
||||
void search(bool plater_is_active, Preset::Type type, wxWindow *tag, TextInput *etag, wxWindow *stag);
|
||||
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 optimize_rotation();
|
||||
// find all empty cells on the plate and won't overlap with exclusion areas
|
||||
|
||||
@@ -525,8 +525,32 @@ void Selection::center()
|
||||
|
||||
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()));
|
||||
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) {
|
||||
@@ -550,11 +574,9 @@ void Selection::set_printable(bool printable)
|
||||
return;
|
||||
|
||||
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 (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it)
|
||||
{
|
||||
instances_idxs.insert(std::make_pair(obj_it->first, *inst_it));
|
||||
for (const auto& [obj_idx, inst_list] : m_cache.content) {
|
||||
for (int inst_idx : inst_list) {
|
||||
instances_idxs.insert({obj_idx, inst_idx});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,15 +584,66 @@ void Selection::set_printable(bool printable)
|
||||
wxGetApp().plater()->take_snapshot(snapshot_text);
|
||||
|
||||
// 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)
|
||||
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
|
||||
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
|
||||
@@ -1601,7 +1674,6 @@ void Selection::scale_and_translate(const Vec3d &scale, const Vec3d &world_trans
|
||||
synchronize_unselected_volumes();
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
|
||||
ensure_on_bed();
|
||||
set_bounding_boxes_dirty();
|
||||
if (wxGetApp().plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasAssembleView) {
|
||||
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);
|
||||
scale_and_translate(mirror, Vec3d::Zero(), transformation_type);
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
// render cumulative bounding box of selected volumes
|
||||
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());
|
||||
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());
|
||||
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 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_auto_drop = auto_drop;
|
||||
|
||||
const Vec3f b_min = box.min.cast<float>();
|
||||
const Vec3f b_max = box.max.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;
|
||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
|
||||
init_data.reserve_vertices(48);
|
||||
init_data.reserve_indices(48);
|
||||
init_data.reserve_vertices(total_vertices);
|
||||
init_data.reserve_indices(total_vertices);
|
||||
|
||||
// vertices
|
||||
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(), 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(), 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()));
|
||||
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() + 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() + size.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(), 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(), 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_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() - 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() + size.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(), 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(), 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_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() + 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() - size.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(), 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(), 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_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() - 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() - size.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(), 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(), 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_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(), 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(), b_min.z()));
|
||||
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z() + size.z()));
|
||||
// Indicate that auto_drop == false, by drawing an arrow on bottom corners
|
||||
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
|
||||
|
||||
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(), 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(), b_min.z()));
|
||||
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z() + size.z()));
|
||||
// 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()}};
|
||||
|
||||
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(), 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(), b_min.z()));
|
||||
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z() + size.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};
|
||||
|
||||
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(), 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(), b_max.z()));
|
||||
init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z() - size.z()));
|
||||
// Stem
|
||||
init_data.add_vertex(base);
|
||||
init_data.add_vertex(tip);
|
||||
|
||||
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(), 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(), b_max.z()));
|
||||
init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z() - size.z()));
|
||||
// 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;
|
||||
|
||||
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(), 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(), b_max.z()));
|
||||
init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z() - size.z()));
|
||||
// Wing 1 (X-Inward)
|
||||
init_data.add_vertex(tip);
|
||||
init_data.add_vertex(Vec3f(tip.x() + dirX, tip.y(), tip.z() - head_s));
|
||||
|
||||
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(), 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(), b_max.z()));
|
||||
init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z() - size.z()));
|
||||
// Wing 2 (Y-Inward)
|
||||
init_data.add_vertex(tip);
|
||||
init_data.add_vertex(Vec3f(tip.x(), tip.y() + dirY, tip.z() - head_s));
|
||||
}
|
||||
}
|
||||
|
||||
// indices
|
||||
for (unsigned int i = 0; i < 48; ++i) {
|
||||
// Finalize indices
|
||||
for (unsigned int i = 0; i < init_data.vertices_count(); ++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)
|
||||
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();
|
||||
|
||||
assert(is_rotation_xy_synchronized(curr_inst_trafo_i, new_inst_trafo_j));
|
||||
volume_j->set_instance_transformation(new_inst_trafo_j);
|
||||
done.insert(j);
|
||||
@@ -2985,17 +3102,25 @@ void Selection::ensure_on_bed()
|
||||
InstancesToZMap instances_min_z;
|
||||
|
||||
for (size_t i = 0; i < m_volumes->size(); ++i) {
|
||||
GLVolume* volume = (*m_volumes)[i];
|
||||
if (!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()) {
|
||||
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;
|
||||
GLVolume* volume = (*m_volumes)[i];
|
||||
ModelObject* mo = m_model->objects[volume->object_idx()];
|
||||
ModelInstance* mi = mo->instances[volume->instance_idx()];
|
||||
|
||||
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) {
|
||||
|
||||
@@ -181,6 +181,7 @@ private:
|
||||
GLModel m_arrow;
|
||||
GLModel m_curved_arrow;
|
||||
GLModel m_box;
|
||||
bool m_auto_drop = true;
|
||||
struct Planes
|
||||
{
|
||||
std::array<Vec3f, 2> check_points{ Vec3f::Zero(), Vec3f::Zero() };
|
||||
@@ -237,6 +238,8 @@ public:
|
||||
void drop();
|
||||
void center_plate(const int plate_idx);
|
||||
void set_printable(bool printable);
|
||||
bool get_auto_drop() const;
|
||||
void set_auto_drop(bool enabled);
|
||||
|
||||
void add_all();
|
||||
void remove_all();
|
||||
@@ -370,7 +373,7 @@ public:
|
||||
|
||||
void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color, float scale) {
|
||||
m_scale_factor = scale;
|
||||
render_bounding_box(box, Transform3d::Identity(), color);
|
||||
render_bounding_box(box, Transform3d::Identity(), get_auto_drop(), color);
|
||||
}
|
||||
|
||||
//BBS
|
||||
@@ -414,7 +417,7 @@ private:
|
||||
m_bounding_sphere.reset();
|
||||
}
|
||||
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_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix);
|
||||
//BBS: GUI refactor: add uniform_scale from gizmo
|
||||
|
||||
Reference in New Issue
Block a user