mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 06:26:06 +08:00
Cut improvements:
* Added new cut() function witch respects to the rotation of the cut plane * Added revert buttons to the GizmoCutDialog * Fixed GLGizmoCenterMove::bounding_box(). Pad and supports don't added to the bb now
This commit is contained in:
parent
9917b8e58b
commit
1b9f42d71b
@ -140,6 +140,8 @@ namespace ImGui
|
|||||||
const wchar_t CancelButton = 0x14;
|
const wchar_t CancelButton = 0x14;
|
||||||
const wchar_t CancelHoverButton = 0x15;
|
const wchar_t CancelHoverButton = 0x15;
|
||||||
// const wchar_t VarLayerHeightMarker = 0x16;
|
// const wchar_t VarLayerHeightMarker = 0x16;
|
||||||
|
const wchar_t RevertButton = 0x16;
|
||||||
|
const wchar_t RevertButton2 = 0x17;
|
||||||
|
|
||||||
const wchar_t RightArrowButton = 0x18;
|
const wchar_t RightArrowButton = 0x18;
|
||||||
const wchar_t RightArrowHoverButton = 0x19;
|
const wchar_t RightArrowHoverButton = 0x19;
|
||||||
|
@ -1336,6 +1336,196 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes)
|
||||||
|
{
|
||||||
|
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
|
||||||
|
|
||||||
|
// Clone the object to duplicate instances, materials etc.
|
||||||
|
ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr;
|
||||||
|
ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr;
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||||
|
upper->set_model(nullptr);
|
||||||
|
upper->sla_support_points.clear();
|
||||||
|
upper->sla_drain_holes.clear();
|
||||||
|
upper->sla_points_status = sla::PointsStatus::NoPoints;
|
||||||
|
upper->clear_volumes();
|
||||||
|
upper->input_file.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||||
|
lower->set_model(nullptr);
|
||||||
|
lower->sla_support_points.clear();
|
||||||
|
lower->sla_drain_holes.clear();
|
||||||
|
lower->sla_points_status = sla::PointsStatus::NoPoints;
|
||||||
|
lower->clear_volumes();
|
||||||
|
lower->input_file.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because transformations are going to be applied to meshes directly,
|
||||||
|
// we reset transformation of all instances and volumes,
|
||||||
|
// except for translation and Z-rotation on instances, which are preserved
|
||||||
|
// in the transformation matrix and not applied to the mesh transform.
|
||||||
|
|
||||||
|
// const auto instance_matrix = instances[instance]->get_matrix(true);
|
||||||
|
const auto instance_matrix = Geometry::assemble_transform(
|
||||||
|
Vec3d::Zero(), // don't apply offset
|
||||||
|
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)),
|
||||||
|
instances[instance]->get_scaling_factor(),
|
||||||
|
instances[instance]->get_mirror()
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto cut_matrix = Geometry::assemble_transform(
|
||||||
|
-cut_center,
|
||||||
|
Vec3d::Zero(),
|
||||||
|
Vec3d::Ones(),
|
||||||
|
Vec3d::Ones()
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto invert_cut_matrix = Geometry::assemble_transform(
|
||||||
|
cut_center,
|
||||||
|
cut_rotation,
|
||||||
|
Vec3d::Ones(),
|
||||||
|
Vec3d::Ones()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Displacement (in instance coordinates) to be applied to place the upper parts
|
||||||
|
Vec3d local_displace = Vec3d::Zero();
|
||||||
|
|
||||||
|
for (ModelVolume* volume : volumes) {
|
||||||
|
const auto volume_matrix = volume->get_matrix();
|
||||||
|
|
||||||
|
volume->supported_facets.reset();
|
||||||
|
volume->seam_facets.reset();
|
||||||
|
volume->mmu_segmentation_facets.reset();
|
||||||
|
|
||||||
|
if (!volume->is_model_part()) {
|
||||||
|
// Modifiers are not cut, but we still need to add the instance transformation
|
||||||
|
// to the modifier volume transformation to preserve their shape properly.
|
||||||
|
|
||||||
|
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||||
|
upper->add_volume(*volume);
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||||
|
lower->add_volume(*volume);
|
||||||
|
}
|
||||||
|
else if (!volume->mesh().empty()) {
|
||||||
|
// Transform the mesh by the combined transformation matrix.
|
||||||
|
// Flip the triangles in case the composite transformation is left handed.
|
||||||
|
TriangleMesh mesh(volume->mesh());
|
||||||
|
mesh.transform(cut_matrix * instance_matrix * volume_matrix, true);
|
||||||
|
mesh.rotate(-cut_rotation.z(), Z);
|
||||||
|
mesh.rotate(-cut_rotation.y(), Y);
|
||||||
|
mesh.rotate(-cut_rotation.x(), X);
|
||||||
|
|
||||||
|
volume->reset_mesh();
|
||||||
|
// Reset volume transformation except for offset
|
||||||
|
const Vec3d offset = volume->get_offset();
|
||||||
|
volume->set_transformation(Geometry::Transformation());
|
||||||
|
volume->set_offset(offset);
|
||||||
|
|
||||||
|
// Perform cut
|
||||||
|
TriangleMesh upper_mesh, lower_mesh;
|
||||||
|
{
|
||||||
|
indexed_triangle_set upper_its, lower_its;
|
||||||
|
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||||
|
upper_mesh = TriangleMesh(upper_its);
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||||
|
lower_mesh = TriangleMesh(lower_its);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) {
|
||||||
|
upper_mesh.transform(invert_cut_matrix);
|
||||||
|
|
||||||
|
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||||
|
vol->name = volume->name;
|
||||||
|
// Don't copy the config's ID.
|
||||||
|
vol->config.assign_config(volume->config);
|
||||||
|
assert(vol->config.id().valid());
|
||||||
|
assert(vol->config.id() != volume->config.id());
|
||||||
|
vol->set_material(volume->material_id(), *volume->material());
|
||||||
|
}
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
|
||||||
|
lower_mesh.transform(invert_cut_matrix);
|
||||||
|
|
||||||
|
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||||
|
vol->name = volume->name;
|
||||||
|
// Don't copy the config's ID.
|
||||||
|
vol->config.assign_config(volume->config);
|
||||||
|
assert(vol->config.id().valid());
|
||||||
|
assert(vol->config.id() != volume->config.id());
|
||||||
|
vol->set_material(volume->material_id(), *volume->material());
|
||||||
|
|
||||||
|
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
|
||||||
|
// The upper part displacement is set to half of the lower part bounding box
|
||||||
|
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||||
|
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObjectPtrs res;
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
|
||||||
|
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||||
|
upper->center_around_origin();
|
||||||
|
upper->translate_instances(-upper->origin_translation);
|
||||||
|
upper->origin_translation = Vec3d::Zero();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
upper->invalidate_bounding_box();
|
||||||
|
upper->center_around_origin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset instance transformation except offset and Z-rotation
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
auto& obj_instance = upper->instances[i];
|
||||||
|
const Vec3d offset = obj_instance->get_offset();
|
||||||
|
const double rot_z = obj_instance->get_rotation().z();
|
||||||
|
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace;
|
||||||
|
|
||||||
|
obj_instance->set_transformation(Geometry::Transformation());
|
||||||
|
obj_instance->set_offset(offset + displace);
|
||||||
|
if (i != instance)
|
||||||
|
obj_instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push_back(upper);
|
||||||
|
}
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
|
||||||
|
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||||
|
lower->center_around_origin();
|
||||||
|
lower->translate_instances(-lower->origin_translation);
|
||||||
|
lower->origin_translation = Vec3d::Zero();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lower->invalidate_bounding_box();
|
||||||
|
lower->center_around_origin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset instance transformation except offset and Z-rotation
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
auto& obj_instance = lower->instances[i];
|
||||||
|
const Vec3d offset = obj_instance->get_offset();
|
||||||
|
const double rot_z = obj_instance->get_rotation().z();
|
||||||
|
obj_instance->set_transformation(Geometry::Transformation());
|
||||||
|
obj_instance->set_offset(offset);
|
||||||
|
obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push_back(lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelObject::split(ModelObjectPtrs* new_objects)
|
void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||||
{
|
{
|
||||||
for (ModelVolume* volume : this->volumes) {
|
for (ModelVolume* volume : this->volumes) {
|
||||||
|
@ -354,6 +354,7 @@ public:
|
|||||||
size_t facets_count() const;
|
size_t facets_count() const;
|
||||||
size_t parts_count() const;
|
size_t parts_count() const;
|
||||||
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
|
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
|
||||||
|
ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes);
|
||||||
void split(ModelObjectPtrs* new_objects);
|
void split(ModelObjectPtrs* new_objects);
|
||||||
void merge();
|
void merge();
|
||||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||||
|
@ -34,6 +34,8 @@ void GLGizmoCenterMove::set_center_pos(const Vec3d& centre_pos)
|
|||||||
set_center(Vec3d(std::clamp(centre_pos.x(), m_min_pos.x(), m_max_pos.x()),
|
set_center(Vec3d(std::clamp(centre_pos.x(), m_min_pos.x(), m_max_pos.x()),
|
||||||
std::clamp(centre_pos.y(), m_min_pos.y(), m_max_pos.y()),
|
std::clamp(centre_pos.y(), m_min_pos.y(), m_max_pos.y()),
|
||||||
std::clamp(centre_pos.z(), m_min_pos.z(), m_max_pos.z())));
|
std::clamp(centre_pos.z(), m_min_pos.z(), m_max_pos.z())));
|
||||||
|
|
||||||
|
m_center_offset = get_center() - m_bb_center;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GLGizmoCenterMove::get_tooltip() const
|
std::string GLGizmoCenterMove::get_tooltip() const
|
||||||
@ -55,14 +57,8 @@ std::string GLGizmoCenterMove::get_tooltip() const
|
|||||||
void GLGizmoCenterMove::on_set_state()
|
void GLGizmoCenterMove::on_set_state()
|
||||||
{
|
{
|
||||||
// Reset internal variables on gizmo activation, if bounding box was changed
|
// Reset internal variables on gizmo activation, if bounding box was changed
|
||||||
if (get_state() == On) {
|
if (get_state() == On)
|
||||||
const BoundingBoxf3 box = bounding_box();
|
update_bb();
|
||||||
if (m_max_pos != box.max && m_min_pos != box.min) {
|
|
||||||
m_max_pos = box.max;
|
|
||||||
m_min_pos = box.min;
|
|
||||||
set_center_pos(box.center());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCenterMove::on_update(const UpdateData& data)
|
void GLGizmoCenterMove::on_update(const UpdateData& data)
|
||||||
@ -78,12 +74,26 @@ BoundingBoxf3 GLGizmoCenterMove::bounding_box() const
|
|||||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||||
for (unsigned int i : idxs) {
|
for (unsigned int i : idxs) {
|
||||||
const GLVolume* volume = selection.get_volume(i);
|
const GLVolume* volume = selection.get_volume(i);
|
||||||
if (!volume->is_modifier)
|
// respect just to the solid parts for FFF and ignore pad and supports for SLA
|
||||||
|
if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support())
|
||||||
ret.merge(volume->transformed_convex_hull_bounding_box());
|
ret.merge(volume->transformed_convex_hull_bounding_box());
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GLGizmoCenterMove::update_bb()
|
||||||
|
{
|
||||||
|
const BoundingBoxf3 box = bounding_box();
|
||||||
|
if (m_max_pos != box.max && m_min_pos != box.min) {
|
||||||
|
m_max_pos = box.max;
|
||||||
|
m_min_pos = box.min;
|
||||||
|
m_bb_center = box.center();
|
||||||
|
set_center_pos(m_bb_center + m_center_offset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
||||||
@ -137,7 +147,7 @@ void GLGizmoCut3D::update_clipper()
|
|||||||
Vec3d plane_center = m_move_gizmo.get_center();
|
Vec3d plane_center = m_move_gizmo.get_center();
|
||||||
BoundingBoxf3 box = m_move_gizmo.bounding_box();
|
BoundingBoxf3 box = m_move_gizmo.bounding_box();
|
||||||
|
|
||||||
Vec3d min, max = min = plane_center = m_move_gizmo.get_center();
|
Vec3d min, max = min = plane_center;
|
||||||
min[Z] = box.min.z();
|
min[Z] = box.min.z();
|
||||||
max[Z] = box.max.z();
|
max[Z] = box.max.z();
|
||||||
|
|
||||||
@ -155,10 +165,17 @@ void GLGizmoCut3D::update_clipper()
|
|||||||
m_c->object_clipper()->set_range_and_pos(beg, end, dist);
|
m_c->object_clipper()->set_range_and_pos(beg, end, dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut3D::update_clipper_on_render()
|
||||||
|
{
|
||||||
|
update_clipper();
|
||||||
|
suppress_update_clipper_on_render = true;
|
||||||
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::set_center(const Vec3d& center)
|
void GLGizmoCut3D::set_center(const Vec3d& center)
|
||||||
{
|
{
|
||||||
m_move_gizmo.set_center_pos(center);
|
m_move_gizmo.set_center_pos(center);
|
||||||
m_rotation_gizmo.set_center(m_move_gizmo.get_center());
|
m_rotation_gizmo.set_center(m_move_gizmo.get_center());
|
||||||
|
update_clipper();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx)
|
void GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx)
|
||||||
@ -253,8 +270,11 @@ void GLGizmoCut3D::render_rotation_input(int axis)
|
|||||||
ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
|
ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
rotation[axis] = Geometry::deg2rad(value);
|
if (double val = Geometry::deg2rad(value); val != rotation[axis]) {
|
||||||
m_rotation_gizmo.set_rotation(rotation);
|
rotation[axis] = val;
|
||||||
|
m_rotation_gizmo.set_rotation(rotation);
|
||||||
|
update_clipper();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type)
|
void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type)
|
||||||
@ -273,6 +293,30 @@ void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode)
|
|||||||
m_connector_mode = mode;
|
m_connector_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GLGizmoCut3D::render_revert_button(const wxString& label)
|
||||||
|
{
|
||||||
|
const ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y });
|
||||||
|
ImGui::SameLine(m_label_width);
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f });
|
||||||
|
|
||||||
|
bool revert = m_imgui->button(label);
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
m_imgui->tooltip(into_u8(_L("Revert")).c_str(), ImGui::GetFontSize() * 20.0f);
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
return revert;
|
||||||
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::render_cut_plane()
|
void GLGizmoCut3D::render_cut_plane()
|
||||||
{
|
{
|
||||||
const BoundingBoxf3 box = m_move_gizmo.bounding_box();
|
const BoundingBoxf3 box = m_move_gizmo.bounding_box();
|
||||||
@ -365,6 +409,8 @@ void GLGizmoCut3D::on_set_state()
|
|||||||
m_move_gizmo.set_state(m_state);
|
m_move_gizmo.set_state(m_state);
|
||||||
m_rotation_gizmo.set_center(m_move_gizmo.get_center());
|
m_rotation_gizmo.set_center(m_move_gizmo.get_center());
|
||||||
m_rotation_gizmo.set_state(m_state);
|
m_rotation_gizmo.set_state(m_state);
|
||||||
|
|
||||||
|
suppress_update_clipper_on_render = m_state != On;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::on_set_hover_id()
|
void GLGizmoCut3D::on_set_hover_id()
|
||||||
@ -388,7 +434,7 @@ void GLGizmoCut3D::on_disable_grabber(unsigned int id)
|
|||||||
|
|
||||||
bool GLGizmoCut3D::on_is_activable() const
|
bool GLGizmoCut3D::on_is_activable() const
|
||||||
{
|
{
|
||||||
return m_move_gizmo.is_activable();
|
return m_rotation_gizmo.is_activable() && m_move_gizmo.is_activable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::on_start_dragging()
|
void GLGizmoCut3D::on_start_dragging()
|
||||||
@ -408,11 +454,16 @@ void GLGizmoCut3D::on_update(const UpdateData& data)
|
|||||||
{
|
{
|
||||||
m_move_gizmo.update(data);
|
m_move_gizmo.update(data);
|
||||||
m_rotation_gizmo.update(data);
|
m_rotation_gizmo.update(data);
|
||||||
|
update_clipper();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::on_render()
|
void GLGizmoCut3D::on_render()
|
||||||
{
|
{
|
||||||
update_clipper();
|
if (m_move_gizmo.update_bb()) {
|
||||||
|
m_rotation_gizmo.set_center(m_move_gizmo.get_center());
|
||||||
|
update_clipper_on_render();
|
||||||
|
}
|
||||||
|
|
||||||
render_cut_plane();
|
render_cut_plane();
|
||||||
if (m_mode == CutMode::cutPlanar) {
|
if (m_mode == CutMode::cutPlanar) {
|
||||||
int move_group_id = m_move_gizmo.get_group_id();
|
int move_group_id = m_move_gizmo.get_group_id();
|
||||||
@ -421,6 +472,9 @@ void GLGizmoCut3D::on_render()
|
|||||||
if (m_hover_id == -1 || m_hover_id >= move_group_id)
|
if (m_hover_id == -1 || m_hover_id >= move_group_id)
|
||||||
m_move_gizmo.render();
|
m_move_gizmo.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!suppress_update_clipper_on_render)
|
||||||
|
update_clipper_on_render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
||||||
@ -449,20 +503,23 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
|
|
||||||
render_combo(_u8L("Mode"), m_modes, m_mode);
|
render_combo(_u8L("Mode"), m_modes, m_mode);
|
||||||
|
|
||||||
|
bool revert_rotation{ false };
|
||||||
|
bool revert_move{ false };
|
||||||
|
|
||||||
if (m_mode <= CutMode::cutByLine) {
|
if (m_mode <= CutMode::cutByLine) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
if (m_mode == CutMode::cutPlanar) {
|
if (m_mode == CutMode::cutPlanar) {
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
m_imgui->text(_L("Move center"));
|
m_imgui->text(_L("Move center"));
|
||||||
ImGui::SameLine(m_label_width);
|
revert_move = render_revert_button(ImGui::RevertButton);
|
||||||
for (Axis axis : {X, Y, Z})
|
for (Axis axis : {X, Y, Z})
|
||||||
render_move_center_input(axis);
|
render_move_center_input(axis);
|
||||||
m_imgui->text(m_imperial_units ? _L("in") : _L("mm"));
|
m_imgui->text(m_imperial_units ? _L("in") : _L("mm"));
|
||||||
|
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
m_imgui->text(_L("Rotation"));
|
m_imgui->text(_L("Rotation"));
|
||||||
ImGui::SameLine(m_label_width);
|
revert_rotation = render_revert_button(ImGui::RevertButton2);
|
||||||
for (Axis axis : {X, Y, Z})
|
for (Axis axis : {X, Y, Z})
|
||||||
render_rotation_input(axis);
|
render_rotation_input(axis);
|
||||||
m_imgui->text(_L("°"));
|
m_imgui->text(_L("°"));
|
||||||
@ -524,7 +581,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
////////
|
////////
|
||||||
static bool hide_clipped = true;
|
static bool hide_clipped = true;
|
||||||
static bool fill_cut = true;
|
static bool fill_cut = true;
|
||||||
static float contour_width = 0.;
|
static float contour_width = 0.2f;
|
||||||
m_imgui->checkbox("hide_clipped", hide_clipped);
|
m_imgui->checkbox("hide_clipped", hide_clipped);
|
||||||
m_imgui->checkbox("fill_cut", fill_cut);
|
m_imgui->checkbox("fill_cut", fill_cut);
|
||||||
m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f);
|
m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f);
|
||||||
@ -535,6 +592,13 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
|
|
||||||
if (cut_clicked && (m_keep_upper || m_keep_lower))
|
if (cut_clicked && (m_keep_upper || m_keep_lower))
|
||||||
perform_cut(m_parent.get_selection());
|
perform_cut(m_parent.get_selection());
|
||||||
|
|
||||||
|
if (revert_move)
|
||||||
|
set_center(m_move_gizmo.bounding_box().center());
|
||||||
|
if (revert_rotation) {
|
||||||
|
m_rotation_gizmo.set_rotation(Vec3d::Zero());
|
||||||
|
update_clipper();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLGizmoCut3D::can_perform_cut() const
|
bool GLGizmoCut3D::can_perform_cut() const
|
||||||
@ -553,8 +617,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
|||||||
const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
|
const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||||
const double object_cut_z = m_move_gizmo.get_center().z() - first_glvolume->get_sla_shift_z();
|
const double object_cut_z = m_move_gizmo.get_center().z() - first_glvolume->get_sla_shift_z();
|
||||||
|
|
||||||
|
Vec3d instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset();
|
||||||
|
|
||||||
|
Vec3d cut_center_offset = m_move_gizmo.get_center() - instance_offset;
|
||||||
|
cut_center_offset[Z] -= first_glvolume->get_sla_shift_z();
|
||||||
|
|
||||||
if (0.0 < object_cut_z && can_perform_cut())
|
if (0.0 < object_cut_z && can_perform_cut())
|
||||||
wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z,
|
wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(),
|
||||||
only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
|
only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
|
||||||
only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) |
|
only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) |
|
||||||
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower));
|
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower));
|
||||||
|
@ -19,8 +19,10 @@ public:
|
|||||||
static const double Margin;
|
static const double Margin;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Vec3d m_min_pos{ Vec3d::Zero() };
|
Vec3d m_min_pos { Vec3d::Zero() };
|
||||||
Vec3d m_max_pos{ Vec3d::Zero() };
|
Vec3d m_max_pos { Vec3d::Zero() };
|
||||||
|
Vec3d m_bb_center { Vec3d::Zero() };
|
||||||
|
Vec3d m_center_offset { Vec3d::Zero() };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||||
@ -33,6 +35,7 @@ protected:
|
|||||||
public:
|
public:
|
||||||
void set_center_pos(const Vec3d& center_pos);
|
void set_center_pos(const Vec3d& center_pos);
|
||||||
BoundingBoxf3 bounding_box() const;
|
BoundingBoxf3 bounding_box() const;
|
||||||
|
bool update_bb();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -56,6 +59,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
float m_label_width{ 150.0 };
|
float m_label_width{ 150.0 };
|
||||||
float m_control_width{ 200.0 };
|
float m_control_width{ 200.0 };
|
||||||
bool m_imperial_units{ false };
|
bool m_imperial_units{ false };
|
||||||
|
bool suppress_update_clipper_on_render{false};
|
||||||
|
|
||||||
enum CutMode {
|
enum CutMode {
|
||||||
cutPlanar
|
cutPlanar
|
||||||
@ -114,11 +118,12 @@ public:
|
|||||||
|
|
||||||
void shift_cut_z(double delta);
|
void shift_cut_z(double delta);
|
||||||
void update_clipper();
|
void update_clipper();
|
||||||
|
void update_clipper_on_render();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool on_init() override;
|
bool on_init() override;
|
||||||
void on_load(cereal::BinaryInputArchive& ar) override { ar(/*m_cut_z, */m_keep_upper, m_keep_lower, m_rotate_lower); }
|
void on_load(cereal::BinaryInputArchive& ar) override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); }
|
||||||
void on_save(cereal::BinaryOutputArchive& ar) const override { ar(/*m_cut_z, */m_keep_upper, m_keep_lower, m_rotate_lower); }
|
void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); }
|
||||||
std::string on_get_name() const override;
|
std::string on_get_name() const override;
|
||||||
void on_set_state() override;
|
void on_set_state() override;
|
||||||
CommonGizmosDataID on_get_requirements() const override;
|
CommonGizmosDataID on_get_requirements() const override;
|
||||||
@ -144,6 +149,7 @@ private:
|
|||||||
void render_move_center_input(int axis);
|
void render_move_center_input(int axis);
|
||||||
void render_rotation_input(int axis);
|
void render_rotation_input(int axis);
|
||||||
void render_connect_mode_radio_button(ConnectorMode mode);
|
void render_connect_mode_radio_button(ConnectorMode mode);
|
||||||
|
bool render_revert_button(const wxString& label);
|
||||||
void render_connect_type_radio_button(ConnectorType type);
|
void render_connect_type_radio_button(ConnectorType type);
|
||||||
bool can_perform_cut() const;
|
bool can_perform_cut() const;
|
||||||
|
|
||||||
|
@ -70,6 +70,8 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||||||
{ImGui::LegendShells , "legend_shells" },
|
{ImGui::LegendShells , "legend_shells" },
|
||||||
{ImGui::LegendToolMarker , "legend_toolmarker" },
|
{ImGui::LegendToolMarker , "legend_toolmarker" },
|
||||||
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
|
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
|
||||||
|
{ImGui::RevertButton , "undo" },
|
||||||
|
{ImGui::RevertButton2 , "undo" },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||||
|
@ -5854,6 +5854,30 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCut
|
|||||||
selection.add_object((unsigned int)(last_id - i), i == 0);
|
selection.add_object((unsigned int)(last_id - i), i == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
|
||||||
|
auto* object = p->model.objects[obj_idx];
|
||||||
|
|
||||||
|
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds");
|
||||||
|
|
||||||
|
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Plater::TakeSnapshot snapshot(this, _L("Cut by Plane"));
|
||||||
|
wxBusyCursor wait;
|
||||||
|
|
||||||
|
const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes);
|
||||||
|
|
||||||
|
remove(obj_idx);
|
||||||
|
p->load_model_objects(new_objects);
|
||||||
|
|
||||||
|
Selection& selection = p->get_selection();
|
||||||
|
size_t last_id = p->model.objects.size() - 1;
|
||||||
|
for (size_t i = 0; i < new_objects.size(); ++i)
|
||||||
|
selection.add_object((unsigned int)(last_id - i), i == 0);
|
||||||
|
}
|
||||||
|
|
||||||
void Plater::export_gcode(bool prefer_removable)
|
void Plater::export_gcode(bool prefer_removable)
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty())
|
if (p->model.objects.empty())
|
||||||
|
@ -254,6 +254,7 @@ public:
|
|||||||
void toggle_layers_editing(bool enable);
|
void toggle_layers_editing(bool enable);
|
||||||
|
|
||||||
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes);
|
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes);
|
||||||
|
void cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes);
|
||||||
|
|
||||||
void export_gcode(bool prefer_removable);
|
void export_gcode(bool prefer_removable);
|
||||||
void export_stl_obj(bool extended = false, bool selection_only = false);
|
void export_stl_obj(bool extended = false, bool selection_only = false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user