mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 11:55:58 +08:00
Cut: Perform cut with connectors
This commit is contained in:
parent
b204f05809
commit
09249e3b8d
@ -1336,6 +1336,53 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes)
|
||||||
|
{
|
||||||
|
if (cut_connectors.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool is_prizm = connector_attributes.style == CutConnectorStyle::Prizm;
|
||||||
|
const std::function<indexed_triangle_set(double, double, double)>& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone;
|
||||||
|
|
||||||
|
indexed_triangle_set connector_mesh;
|
||||||
|
switch (CutConnectorShape(connector_attributes.shape)) {
|
||||||
|
case CutConnectorShape::Triangle:
|
||||||
|
connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 3));
|
||||||
|
break;
|
||||||
|
case CutConnectorShape::Square:
|
||||||
|
connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 4));
|
||||||
|
break;
|
||||||
|
case CutConnectorShape::Circle:
|
||||||
|
connector_mesh = its_make_shape(1.0, 1.0, 2 * PI / 360);
|
||||||
|
break;
|
||||||
|
case CutConnectorShape::Hexagon:
|
||||||
|
connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 6));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t connector_id = 0;
|
||||||
|
|
||||||
|
for (const CutConnector& connector : cut_connectors) {
|
||||||
|
TriangleMesh mesh = TriangleMesh(connector_mesh);
|
||||||
|
// Mesh will be centered when loading.
|
||||||
|
ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
|
||||||
|
|
||||||
|
// Transform the new modifier to be aligned inside the instance
|
||||||
|
new_volume->set_transformation(Geometry::assemble_transform(
|
||||||
|
connector.pos,
|
||||||
|
connector.rotation,
|
||||||
|
Vec3d(connector.radius, connector.radius, connector.height),
|
||||||
|
Vec3d::Ones()
|
||||||
|
));
|
||||||
|
|
||||||
|
new_volume->name = name + "-" + std::to_string(++connector_id);
|
||||||
|
new_volume->source.is_connector = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete all connectors
|
||||||
|
cut_connectors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes)
|
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))
|
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||||
@ -1405,15 +1452,31 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
|
|||||||
if (!volume->is_model_part()) {
|
if (!volume->is_model_part()) {
|
||||||
// Modifiers are not cut, but we still need to add the instance transformation
|
// Modifiers are not cut, but we still need to add the instance transformation
|
||||||
// to the modifier volume transformation to preserve their shape properly.
|
// to the modifier volume transformation to preserve their shape properly.
|
||||||
|
// But if this modifier is a connector, then just set volume transformation
|
||||||
|
if (volume->source.is_connector)
|
||||||
|
volume->set_transformation(Geometry::Transformation(volume_matrix));
|
||||||
|
else
|
||||||
|
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
|
||||||
|
|
||||||
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
|
ModelVolume* vol = { nullptr };
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
ModelVolume* vol = upper->add_volume(*volume);
|
||||||
upper->add_volume(*volume);
|
if (volume->source.is_connector)
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
vol->source.is_connector = true;
|
||||||
lower->add_volume(*volume);
|
}
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||||
|
ModelVolume* vol = lower->add_volume(*volume);
|
||||||
|
if (volume->source.is_connector) {
|
||||||
|
vol->source.is_connector = true;
|
||||||
|
// for lower part change type of conector from NEGATIVE_VOLUME to MODEL_PART
|
||||||
|
if (vol->type() == ModelVolumeType::NEGATIVE_VOLUME)
|
||||||
|
vol->set_type(ModelVolumeType::MODEL_PART);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!volume->mesh().empty()) {
|
else if (!volume->mesh().empty() &&
|
||||||
|
!volume->source.is_connector // we don't allow to cut a connectors
|
||||||
|
) {
|
||||||
// Transform the mesh by the combined transformation matrix.
|
// Transform the mesh by the combined transformation matrix.
|
||||||
// Flip the triangles in case the composite transformation is left handed.
|
// Flip the triangles in case the composite transformation is left handed.
|
||||||
TriangleMesh mesh(volume->mesh());
|
TriangleMesh mesh(volume->mesh());
|
||||||
|
@ -221,22 +221,22 @@ private:
|
|||||||
|
|
||||||
struct CutConnector
|
struct CutConnector
|
||||||
{
|
{
|
||||||
Vec3f pos;
|
Vec3d pos;
|
||||||
Vec3f normal;
|
Vec3d rotation;
|
||||||
float radius;
|
float radius;
|
||||||
float height;
|
float height;
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
|
||||||
CutConnector()
|
CutConnector()
|
||||||
: pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f)
|
: pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CutConnector(Vec3f p, Vec3f n, float r, float h, bool fl = false)
|
CutConnector(Vec3d p, Vec3d n, float r, float h, bool fl = false)
|
||||||
: pos(p), normal(n), radius(r), height(h), failed(fl)
|
: pos(p), rotation(n), radius(r), height(h), failed(fl)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CutConnector(const CutConnector& rhs) :
|
CutConnector(const CutConnector& rhs) :
|
||||||
CutConnector(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {}
|
CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.failed) {}
|
||||||
|
|
||||||
bool operator==(const CutConnector& sp) const;
|
bool operator==(const CutConnector& sp) const;
|
||||||
|
|
||||||
@ -251,22 +251,12 @@ struct CutConnector
|
|||||||
*/
|
*/
|
||||||
template<class Archive> inline void serialize(Archive& ar)
|
template<class Archive> inline void serialize(Archive& ar)
|
||||||
{
|
{
|
||||||
ar(pos, normal, radius, height, failed);
|
ar(pos, rotation, radius, height, failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr size_t steps = 32;
|
static constexpr size_t steps = 32;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Declared outside of ModelVolume, so it could be forward declared.
|
|
||||||
enum class ModelVolumeType : int {
|
|
||||||
INVALID = -1,
|
|
||||||
MODEL_PART = 0,
|
|
||||||
NEGATIVE_VOLUME,
|
|
||||||
PARAMETER_MODIFIER,
|
|
||||||
SUPPORT_BLOCKER,
|
|
||||||
SUPPORT_ENFORCER,
|
|
||||||
};
|
|
||||||
|
|
||||||
using CutConnectors = std::vector<CutConnector>;
|
using CutConnectors = std::vector<CutConnector>;
|
||||||
|
|
||||||
enum class CutConnectorType : int {
|
enum class CutConnectorType : int {
|
||||||
@ -288,6 +278,32 @@ enum class CutConnectorShape : int {
|
|||||||
//,D-shape
|
//,D-shape
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CutConnectorAttributes
|
||||||
|
{
|
||||||
|
CutConnectorType type{ CutConnectorType::Plug };
|
||||||
|
CutConnectorStyle style{ CutConnectorStyle::Prizm};
|
||||||
|
CutConnectorShape shape{CutConnectorShape::Circle};
|
||||||
|
|
||||||
|
CutConnectorAttributes() {}
|
||||||
|
|
||||||
|
CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh)
|
||||||
|
: type(t), style(st), shape(sh)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CutConnectorAttributes(const CutConnectorAttributes& rhs) :
|
||||||
|
CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Declared outside of ModelVolume, so it could be forward declared.
|
||||||
|
enum class ModelVolumeType : int {
|
||||||
|
INVALID = -1,
|
||||||
|
MODEL_PART = 0,
|
||||||
|
NEGATIVE_VOLUME,
|
||||||
|
PARAMETER_MODIFIER,
|
||||||
|
SUPPORT_BLOCKER,
|
||||||
|
SUPPORT_ENFORCER,
|
||||||
|
};
|
||||||
|
|
||||||
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower };
|
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower };
|
||||||
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
||||||
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
||||||
@ -416,6 +432,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);
|
||||||
|
void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes);
|
||||||
ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, 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();
|
||||||
@ -671,10 +688,12 @@ public:
|
|||||||
bool is_converted_from_meters{ false };
|
bool is_converted_from_meters{ false };
|
||||||
bool is_from_builtin_objects{ false };
|
bool is_from_builtin_objects{ false };
|
||||||
|
|
||||||
|
bool is_connector{ false };
|
||||||
|
|
||||||
template<class Archive> void serialize(Archive& ar) {
|
template<class Archive> void serialize(Archive& ar) {
|
||||||
//FIXME Vojtech: Serialize / deserialize only if the Source is set.
|
//FIXME Vojtech: Serialize / deserialize only if the Source is set.
|
||||||
// likely testing input_file or object_idx would be sufficient.
|
// likely testing input_file or object_idx would be sufficient.
|
||||||
ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects);
|
ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects, is_connector);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Source source;
|
Source source;
|
||||||
|
@ -578,22 +578,22 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data)
|
|||||||
if (wxGetKeyState(WXK_SHIFT))
|
if (wxGetKeyState(WXK_SHIFT))
|
||||||
projection = m_snap_step * (double)std::round(projection / m_snap_step);
|
projection = m_snap_step * (double)std::round(projection / m_snap_step);
|
||||||
|
|
||||||
|
Vec3d shift = starting_vec * projection;
|
||||||
|
|
||||||
// move cut plane center
|
// move cut plane center
|
||||||
set_center(starting_box_center + starting_vec * projection);
|
set_center(starting_box_center + shift);
|
||||||
|
|
||||||
// move connectors
|
// move connectors
|
||||||
Vec3f shift = Vec3f(starting_vec.cast<float>() * projection);
|
|
||||||
for (auto& connector : connectors)
|
for (auto& connector : connectors)
|
||||||
connector.pos += shift;
|
connector.pos += shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (m_hover_id > m_group_id)
|
else if (m_hover_id > m_group_id)
|
||||||
{
|
{
|
||||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
std::pair<Vec3d, Vec3d> pos_and_normal;
|
||||||
if (!unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos_and_normal))
|
if (!unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos_and_normal))
|
||||||
return;
|
return;
|
||||||
connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first;
|
connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first;
|
||||||
connectors[m_hover_id - m_connectors_group_id].normal = -pos_and_normal.second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,6 +924,28 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
|||||||
cut_center_offset[Z] -= first_glvolume->get_sla_shift_z();
|
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()) {
|
||||||
|
ModelObject* mo = wxGetApp().plater()->model().objects[object_idx];
|
||||||
|
// update connectors pos as offset of its center before cut performing
|
||||||
|
if (!mo->cut_connectors.empty()) {
|
||||||
|
const std::string name = _u8L("Connector");
|
||||||
|
for (CutConnector& connector : mo->cut_connectors) {
|
||||||
|
connector.rotation = m_rotation_gizmo.get_rotation();
|
||||||
|
|
||||||
|
// culculate shift of the connector center regarding to the position on the cut plane
|
||||||
|
Vec3d norm = m_grabbers[0].center - m_plane_center;
|
||||||
|
norm.normalize();
|
||||||
|
Vec3d shift = norm * (0.5 * connector.height);
|
||||||
|
|
||||||
|
// culculate offset of the connector pos regarding to the instance offset and possible SLA elevation
|
||||||
|
Vec3d connector_offset = connector.pos - instance_offset;
|
||||||
|
connector_offset[Z] -= first_glvolume->get_sla_shift_z();
|
||||||
|
|
||||||
|
// Update connector pos. It will be used as a center of created modifiers
|
||||||
|
connector.pos = connector_offset + shift;
|
||||||
|
}
|
||||||
|
mo->apply_cut_connectors(name, CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id)));
|
||||||
|
}
|
||||||
|
|
||||||
wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(),
|
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) |
|
||||||
@ -939,7 +961,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
|||||||
|
|
||||||
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
|
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
|
||||||
// Return false if no intersection was found, true otherwise.
|
// Return false if no intersection was found, true otherwise.
|
||||||
bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair<Vec3f, Vec3f>& pos_and_normal)
|
bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair<Vec3d, Vec3d>& pos_and_normal)
|
||||||
{
|
{
|
||||||
if (!m_c->raycaster()->raycaster())
|
if (!m_c->raycaster()->raycaster())
|
||||||
return false;
|
return false;
|
||||||
@ -965,7 +987,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair
|
|||||||
nullptr, &clipping_plane_was_hit);
|
nullptr, &clipping_plane_was_hit);
|
||||||
if (clipping_plane_was_hit) {
|
if (clipping_plane_was_hit) {
|
||||||
// Return both the point and the facet normal.
|
// Return both the point and the facet normal.
|
||||||
pos_and_normal = std::make_pair(hit, normal);
|
pos_and_normal = std::make_pair(hit.cast<double>(), normal.cast<double>());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1031,10 +1053,10 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
|
|||||||
|
|
||||||
// If there is some selection, don't add new point and deselect everything instead.
|
// If there is some selection, don't add new point and deselect everything instead.
|
||||||
if (m_selection_empty) {
|
if (m_selection_empty) {
|
||||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
std::pair<Vec3d, Vec3d> pos_and_normal;
|
||||||
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos_and_normal)) {
|
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos_and_normal)) {
|
||||||
const Vec3f& hit = pos_and_normal.first;
|
const Vec3d& hit = pos_and_normal.first;
|
||||||
const Vec3f& normal = pos_and_normal.second;
|
const Vec3d& normal = pos_and_normal.second;
|
||||||
// The clipping plane was clicked, hit containts coordinates of the hit in world coords.
|
// The clipping plane was clicked, hit containts coordinates of the hit in world coords.
|
||||||
std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl;
|
std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl;
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin"));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin"));
|
||||||
|
@ -93,7 +93,7 @@ public:
|
|||||||
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||||
|
|
||||||
std::string get_tooltip() const override;
|
std::string get_tooltip() const override;
|
||||||
bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
|
bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair<Vec3d, Vec3d>& pos_and_normal);
|
||||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user