mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 13:16:00 +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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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()) {
|
||||
// Modifiers are not cut, but we still need to add the instance transformation
|
||||
// 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));
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
upper->add_volume(*volume);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower->add_volume(*volume);
|
||||
ModelVolume* vol = { nullptr };
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||
ModelVolume* vol = upper->add_volume(*volume);
|
||||
if (volume->source.is_connector)
|
||||
vol->source.is_connector = true;
|
||||
}
|
||||
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.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
|
@ -221,22 +221,22 @@ private:
|
||||
|
||||
struct CutConnector
|
||||
{
|
||||
Vec3f pos;
|
||||
Vec3f normal;
|
||||
Vec3d pos;
|
||||
Vec3d rotation;
|
||||
float radius;
|
||||
float height;
|
||||
bool failed = false;
|
||||
|
||||
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)
|
||||
: pos(p), normal(n), radius(r), height(h), failed(fl)
|
||||
CutConnector(Vec3d p, Vec3d n, float r, float h, bool fl = false)
|
||||
: pos(p), rotation(n), radius(r), height(h), failed(fl)
|
||||
{}
|
||||
|
||||
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;
|
||||
|
||||
@ -251,22 +251,12 @@ struct CutConnector
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
// 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>;
|
||||
|
||||
enum class CutConnectorType : int {
|
||||
@ -288,6 +278,32 @@ enum class CutConnectorShape : int {
|
||||
//,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 };
|
||||
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
||||
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
||||
@ -416,6 +432,7 @@ public:
|
||||
size_t facets_count() const;
|
||||
size_t parts_count() const;
|
||||
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);
|
||||
void split(ModelObjectPtrs* new_objects);
|
||||
void merge();
|
||||
@ -671,10 +688,12 @@ public:
|
||||
bool is_converted_from_meters{ false };
|
||||
bool is_from_builtin_objects{ false };
|
||||
|
||||
bool is_connector{ false };
|
||||
|
||||
template<class Archive> void serialize(Archive& ar) {
|
||||
//FIXME Vojtech: Serialize / deserialize only if the Source is set.
|
||||
// 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;
|
||||
|
@ -578,22 +578,22 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data)
|
||||
if (wxGetKeyState(WXK_SHIFT))
|
||||
projection = m_snap_step * (double)std::round(projection / m_snap_step);
|
||||
|
||||
Vec3d shift = starting_vec * projection;
|
||||
|
||||
// move cut plane center
|
||||
set_center(starting_box_center + starting_vec * projection);
|
||||
set_center(starting_box_center + shift);
|
||||
|
||||
// move connectors
|
||||
Vec3f shift = Vec3f(starting_vec.cast<float>() * projection);
|
||||
for (auto& connector : connectors)
|
||||
connector.pos += shift;
|
||||
}
|
||||
|
||||
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))
|
||||
return;
|
||||
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();
|
||||
|
||||
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(),
|
||||
only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
|
||||
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
|
||||
// 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())
|
||||
return false;
|
||||
@ -965,7 +987,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair
|
||||
nullptr, &clipping_plane_was_hit);
|
||||
if (clipping_plane_was_hit) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@ -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 (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)) {
|
||||
const Vec3f& hit = pos_and_normal.first;
|
||||
const Vec3f& normal = pos_and_normal.second;
|
||||
const Vec3d& hit = pos_and_normal.first;
|
||||
const Vec3d& normal = pos_and_normal.second;
|
||||
// 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;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
|
Loading…
x
Reference in New Issue
Block a user