* Processed Auto/Manual connetor's mode
* Processed Dowel type of connectors
* Added TriangeMesh::its_make_frustum_dowel
This commit is contained in:
YuSanka 2022-03-22 11:25:48 +01:00
parent 9658c9c6b7
commit 301d0d5288
6 changed files with 206 additions and 81 deletions

View File

@ -712,6 +712,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t
ModelVolume* v = new ModelVolume(this, other);
if (type != ModelVolumeType::INVALID && v->type() != type)
v->set_type(type);
v->source.is_connector = other.source.is_connector;
this->volumes.push_back(v);
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
// v->center_geometry_after_creation();
@ -1336,29 +1337,42 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
return res;
}
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
int sectorCount;
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
break;
case CutConnectorShape::Square:
sectorCount = 4;
break;
case CutConnectorShape::Circle:
sectorCount = 360;
break;
case CutConnectorShape::Hexagon:
sectorCount = 6;
break;
}
if (connector_attributes.style == CutConnectorStyle::Prizm)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
return connector_mesh;
}
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;
}
indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes);
size_t connector_id = 0;
@ -1393,6 +1407,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
// 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;
ModelObject* dowels = attributes.has(ModelObjectCutAttribute::CreateDowels) ? ModelObject::new_clone(*this) : nullptr;
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
upper->set_model(nullptr);
@ -1412,6 +1427,15 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
lower->input_file.clear();
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
dowels->set_model(nullptr);
dowels->sla_support_points.clear();
dowels->sla_drain_holes.clear();
dowels->sla_points_status = sla::PointsStatus::NoPoints;
dowels->clear_volumes();
dowels->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
@ -1450,29 +1474,31 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
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.
// 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));
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)
if (volume->source.is_connector) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
ModelVolume* vol = upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume* vol = lower->add_volume(*volume);
if (!attributes.has(ModelObjectCutAttribute::CreateDowels))
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
// add one more solid part same as connector if this connector is a dowel
// But discard rotation and Z-offset for this volume
volume->set_rotation(Vec3d::Zero());
Vec3d offset = volume->get_offset();
offset[Z] = 0.0;
volume->set_offset(offset);
ModelVolume* vol = dowels->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
}
}
else
// 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));
}
else if (!volume->mesh().empty()
// && !volume->source.is_connector // we don't allow to cut a connectors
@ -1584,6 +1610,32 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels->volumes.size() > 0) {
if (!dowels->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
dowels->center_around_origin();
dowels->translate_instances(-dowels->origin_translation);
dowels->origin_translation = Vec3d::Zero();
}
else {
dowels->invalidate_bounding_box();
dowels->center_around_origin();
}
dowels->name += "-Dowels";
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); ++i) {
auto& obj_instance = dowels->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(0.0, 0.0, i == instance ? 0.0 : rot_z));
}
res.push_back(dowels);
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
return res;

View File

@ -304,7 +304,7 @@ enum class ModelVolumeType : int {
SUPPORT_ENFORCER,
};
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower };
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower, CreateDowels };
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
@ -432,6 +432,7 @@ public:
size_t facets_count() const;
size_t parts_count() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_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);

View File

@ -1053,6 +1053,61 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
return mesh;
}
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
// Note: This function uses code for sphere generation, but for stackCount = 2;
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
{
int stackCount = 2;
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
indexed_triangle_set mesh;
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return mesh;
}
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
{
std::vector<Vec3f> dst_vertices;

View File

@ -302,6 +302,7 @@ indexed_triangle_set its_make_cube(double x, double y, double z);
indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);

View File

@ -326,8 +326,10 @@ void GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type)
{
ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 2*m_label_width);
ImGui::PushItemWidth(m_control_width);
if (m_imgui->radio_button(m_connector_types[int(type)], m_connector_type == type))
if (m_imgui->radio_button(m_connector_types[size_t(type)], m_connector_type == type)) {
m_connector_type = type;
update_connector_shape();
}
}
void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode)
@ -606,7 +608,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data)
set_center(starting_box_center + shift);
}
else if (m_hover_id > m_group_id)
else if (m_hover_id > m_group_id && m_connector_mode == CutConnectorMode::Manual)
{
std::pair<Vec3d, Vec3d> pos_and_normal;
if (!unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos_and_normal))
@ -669,7 +671,7 @@ void GLGizmoCut3D::on_render()
if (!m_hide_cut_plane) {
render_cut_plane();
render_cut_center_graber();
if (m_mode == CutMode::cutPlanar) {
if (m_mode == size_t(CutMode::cutPlanar)) {
if (m_hover_id < m_group_id)
m_rotation_gizmo.render();
}
@ -716,10 +718,12 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
bool revert_rotation{ false };
bool revert_move{ false };
if (m_mode <= CutMode::cutByLine) {
CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
if (m_mode <= size_t(CutMode::cutByLine)) {
ImGui::Separator();
if (m_mode == CutMode::cutPlanar) {
if (m_mode == size_t(CutMode::cutPlanar)) {
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Move center"));
revert_move = render_revert_button("move");
@ -748,11 +752,18 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("After cut"));
ImGui::SameLine(m_label_width);
m_imgui->checkbox(_L("Keep upper part"), m_keep_upper);
m_imgui->disabled_begin(!connectors.empty());
bool keep = true;
m_imgui->checkbox(_L("Keep upper part"), connectors.empty() ? m_keep_upper : keep);
m_imgui->text("");
ImGui::SameLine(m_label_width);
m_imgui->checkbox(_L("Keep lower part"), m_keep_lower);
m_imgui->checkbox(_L("Keep lower part"), connectors.empty() ? m_keep_lower : keep);
m_imgui->text("");
m_imgui->disabled_end();
ImGui::SameLine(m_label_width);
m_imgui->disabled_begin(!m_keep_lower);
m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower);
@ -779,7 +790,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id))
update_connector_shape();
CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio))
for (auto& connector : connectors)
connector.height = float(m_connector_depth_ratio);
@ -829,6 +839,9 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
void GLGizmoCut3D::render_connectors(bool picking)
{
if (m_connector_mode == CutConnectorMode::Auto)
return;
#if ENABLE_LEGACY_OPENGL_REMOVAL
#if ENABLE_GL_SHADERS_ATTRIBUTES
GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr");
@ -863,6 +876,9 @@ void GLGizmoCut3D::render_connectors(bool picking)
const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset();
const float sla_shift = m_c->selection_info()->get_sla_shift();
const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane();
const Vec3d& normal = cp ? cp->get_normal() : Vec3d::Ones();
for (size_t i = 0; i < cache_size; ++i) {
const CutConnector& connector = connectors[i];
const bool& point_selected = m_selected[i];
@ -883,15 +899,21 @@ void GLGizmoCut3D::render_connectors(bool picking)
const_cast<GLModel*>(&m_connector_shape)->set_color(-1, render_color);
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
double height = connector.height;
// recalculate connector position to world position
Vec3d pos = connector.pos + instance_offset;
if (m_connector_type == CutConnectorType::Dowel &&
m_connector_style == size_t(CutConnectorStyle::Prizm)) {
pos -= height * normal;
height *= 2;
}
pos[Z] += sla_shift;
#if ENABLE_GL_SHADERS_ATTRIBUTES
const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform(
Vec3d(pos.x(), pos.y(), pos.z()),
m_rotation_gizmo.get_rotation(),
Vec3d(connector.radius, connector.radius, connector.height),
Vec3d(connector.radius, connector.radius, height),
Vec3d::Ones()
);
shader->set_uniform("view_model_matrix", view_model_matrix);
@ -906,7 +928,7 @@ void GLGizmoCut3D::render_connectors(bool picking)
glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0));
glsafe(::glTranslated(0., 0., -0.5*connector.height));
glsafe(::glScaled(connector.radius, connector.radius, connector.height));
glsafe(::glScaled(connector.radius, connector.radius, height));
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
m_connector_shape.render();
@ -947,26 +969,37 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
Vec3d cut_center_offset = m_plane_center - instance_offset;
cut_center_offset[Z] -= first_glvolume->get_sla_shift_z();
bool create_dowels_as_separate_object = false;
if (0.0 < object_cut_z && can_perform_cut()) {
ModelObject* mo = wxGetApp().plater()->model().objects[object_idx];
const bool has_connectors = !mo->cut_connectors.empty();
// update connectors pos as offset of its center before cut performing
if (!mo->cut_connectors.empty()) {
if (has_connectors && m_connector_mode == CutConnectorMode::Manual) {
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);
connector.pos += shift;
if (m_connector_style == size_t(CutConnectorStyle::Prizm)) {
if (m_connector_type == CutConnectorType::Dowel)
connector.height *= 2;
else {
// 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);
connector.pos += shift;
}
}
}
mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id)));
if (m_connector_type == CutConnectorType::Dowel)
create_dowels_as_separate_object = true;
}
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) |
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower));
only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) |
only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) |
only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels));
m_selected.clear();
}
else {
@ -1025,24 +1058,7 @@ void GLGizmoCut3D::update_connector_shape()
if (m_connector_shape.is_initialized())
m_connector_shape.reset();
bool is_prizm = m_connector_style == size_t(CutConnectorStyle::Prizm);
const std::function<indexed_triangle_set(double, double, double)>& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone;
switch (CutConnectorShape(m_connector_shape_id)) {
case CutConnectorShape::Triangle:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 3)));
break;
case CutConnectorShape::Square:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 4)));
break;
case CutConnectorShape::Circle:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, 2 * PI / 360));
break;
case CutConnectorShape::Hexagon:
m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 6)));
break;
}
m_connector_shape.init_from(ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }));
}
void GLGizmoCut3D::update_model_object() const
@ -1056,7 +1072,7 @@ void GLGizmoCut3D::update_model_object() const
bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{
if (is_dragging())
if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower))
return false;
CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;

View File

@ -59,7 +59,7 @@ class GLGizmoCut3D : public GLGizmoBase
Matrix3d m_rotation_matrix;
Vec3d m_rotations{ Vec3d::Zero() };
enum CutMode {
enum class CutMode {
cutPlanar
, cutByLine
, cutGrig
@ -67,16 +67,16 @@ class GLGizmoCut3D : public GLGizmoBase
//,cutModular
};
enum CutConnectorMode {
enum class CutConnectorMode {
Auto
, Manual
};
std::vector<std::string> m_modes;
size_t m_mode{ size_t(cutPlanar) };
size_t m_mode{ size_t(CutMode::cutPlanar) };
std::vector<std::string> m_connector_modes;
CutConnectorMode m_connector_mode{ Auto };
CutConnectorMode m_connector_mode{ CutConnectorMode::Manual };
std::vector<std::string> m_connector_types;
CutConnectorType m_connector_type;