Cut: Perform cut with connectors

This commit is contained in:
YuSanka 2022-03-15 17:08:15 +01:00
parent b204f05809
commit 09249e3b8d
4 changed files with 139 additions and 35 deletions

View File

@ -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));
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;
}
else if (!volume->mesh().empty()) {
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() &&
!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());

View File

@ -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;

View File

@ -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"));

View File

@ -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>