Cut: contours indexing no longer breaks when the normal is inverted,
detection of the situation when all parts are assigned to one side
This commit is contained in:
parent
44aa32d6e2
commit
ab0ade0539
@ -1563,6 +1563,21 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GLGizmoCut3D::PartSelection::is_one_object() const
|
||||||
|
{
|
||||||
|
// In theory, the implementation could be just this:
|
||||||
|
// return m_contour_to_parts.size() == m_ignored_contours.size();
|
||||||
|
// However, this would require that the part-contour correspondence works
|
||||||
|
// flawlessly. Because it is currently not always so for self-intersecting
|
||||||
|
// objects, let's better check the parts itself:
|
||||||
|
if (m_parts.size() < 2)
|
||||||
|
return true;
|
||||||
|
return std::all_of(m_parts.begin(), m_parts.end(), [this](const Part& part) {
|
||||||
|
return part.selected == m_parts.front().selected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
|
void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
|
||||||
{
|
{
|
||||||
// FIXME: Cache the transforms.
|
// FIXME: Cache the transforms.
|
||||||
@ -1984,7 +1999,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||||||
|
|
||||||
add_vertical_scaled_interval(0.75f);
|
add_vertical_scaled_interval(0.75f);
|
||||||
|
|
||||||
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts);
|
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object()));
|
||||||
if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors")))
|
if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors")))
|
||||||
set_connectors_editing(true);
|
set_connectors_editing(true);
|
||||||
m_imgui->disabled_end();
|
m_imgui->disabled_end();
|
||||||
@ -2404,6 +2419,8 @@ bool GLGizmoCut3D::can_perform_cut() const
|
|||||||
{
|
{
|
||||||
if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing)
|
if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing)
|
||||||
return false;
|
return false;
|
||||||
|
if (m_part_selection.valid())
|
||||||
|
return ! m_part_selection.is_one_object();
|
||||||
|
|
||||||
return true;// has_valid_contour();
|
return true;// has_valid_contour();
|
||||||
}
|
}
|
||||||
|
@ -155,20 +155,20 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
void turn_over_selection();
|
void turn_over_selection();
|
||||||
ModelObject* model_object() { return m_model.objects.front(); }
|
ModelObject* model_object() { return m_model.objects.front(); }
|
||||||
bool valid() const { return m_valid; }
|
bool valid() const { return m_valid; }
|
||||||
|
bool is_one_object() const;
|
||||||
const std::vector<Part>& parts() const { return m_parts; }
|
const std::vector<Part>& parts() const { return m_parts; }
|
||||||
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
|
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
|
||||||
|
|
||||||
std::vector<Vec3d> m_contour_points; // TEMPORARILY PUBLIC
|
|
||||||
std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below
|
|
||||||
std::vector<size_t> m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object)
|
|
||||||
std::vector<std::vector<Vec3d>> m_debug_pts;
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model m_model;
|
Model m_model;
|
||||||
int m_instance_idx;
|
int m_instance_idx;
|
||||||
std::vector<Part> m_parts;
|
std::vector<Part> m_parts;
|
||||||
bool m_valid = false;
|
bool m_valid = false;
|
||||||
|
std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below
|
||||||
|
std::vector<size_t> m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object)
|
||||||
|
|
||||||
|
std::vector<Vec3d> m_contour_points; // Debugging
|
||||||
|
std::vector<std::vector<Vec3d>> m_debug_pts; // Debugging
|
||||||
};
|
};
|
||||||
|
|
||||||
PartSelection m_part_selection;
|
PartSelection m_part_selection;
|
||||||
|
@ -174,13 +174,40 @@ bool MeshClipper::has_valid_contour() const
|
|||||||
|
|
||||||
std::vector<Vec3d> MeshClipper::point_per_contour() const
|
std::vector<Vec3d> MeshClipper::point_per_contour() const
|
||||||
{
|
{
|
||||||
|
assert(m_result);
|
||||||
std::vector<Vec3d> out;
|
std::vector<Vec3d> out;
|
||||||
if (!m_result || m_result->cut_islands.empty())
|
|
||||||
return out;
|
|
||||||
|
|
||||||
for (const CutIsland& isl : m_result->cut_islands) {
|
for (const CutIsland& isl : m_result->cut_islands) {
|
||||||
// FIXME: There might be holes !
|
assert(isl.expoly.contour.size() > 2);
|
||||||
Vec2d c = unscale(isl.expoly.contour.centroid());
|
// Now return a point lying inside the contour but not in a hole.
|
||||||
|
// We do this by taking a point lying close to the edge, repeating
|
||||||
|
// this several times for different edges and distances from them.
|
||||||
|
// (We prefer point not extremely close to the border.
|
||||||
|
bool done = false;
|
||||||
|
Vec2d p;
|
||||||
|
size_t i = 1;
|
||||||
|
while (i < isl.expoly.contour.size()) {
|
||||||
|
const Vec2d& a = unscale(isl.expoly.contour.points[i-1]);
|
||||||
|
const Vec2d& b = unscale(isl.expoly.contour.points[i]);
|
||||||
|
Vec2d n = (b-a).normalized();
|
||||||
|
std::swap(n.x(), n.y());
|
||||||
|
n.x() = -1 * n.x();
|
||||||
|
double f = 10.;
|
||||||
|
while (f > 0.05) {
|
||||||
|
p = (0.5*(b+a)) + f * n;
|
||||||
|
if (isl.expoly.contains(Point::new_scale(p))) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
f = f/10.;
|
||||||
|
}
|
||||||
|
if (done)
|
||||||
|
break;
|
||||||
|
i += isl.expoly.contour.size() / 5;
|
||||||
|
}
|
||||||
|
// If the above failed, just return the centroid, regardless of whether
|
||||||
|
// it is inside the contour or in a hole (we must return something).
|
||||||
|
Vec2d c = done ? p : unscale(isl.expoly.contour.centroid());
|
||||||
out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.));
|
out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
@ -366,9 +393,20 @@ void MeshClipper::recalculate_triangles()
|
|||||||
|
|
||||||
isl.expoly = std::move(exp);
|
isl.expoly = std::move(exp);
|
||||||
isl.expoly_bb = get_extents(exp);
|
isl.expoly_bb = get_extents(exp);
|
||||||
|
isl.hash = 0;
|
||||||
|
for (const Point& pt : isl.expoly.contour) {
|
||||||
|
isl.hash ^= pt.x();
|
||||||
|
isl.hash ^= pt.y();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now sort the islands so they are in defined order. This is a hack needed by cut gizmo, which sometimes
|
||||||
|
// flips the normal of the cut, in which case the contours stay the same but their order may change.
|
||||||
|
std::sort(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& a, const CutIsland& b) {
|
||||||
|
return a.hash < b.hash;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const
|
Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const
|
||||||
{
|
{
|
||||||
|
@ -141,6 +141,7 @@ private:
|
|||||||
ExPolygon expoly;
|
ExPolygon expoly;
|
||||||
BoundingBox expoly_bb;
|
BoundingBox expoly_bb;
|
||||||
bool disabled = false;
|
bool disabled = false;
|
||||||
|
coord_t hash;
|
||||||
};
|
};
|
||||||
struct ClipResult {
|
struct ClipResult {
|
||||||
std::vector<CutIsland> cut_islands;
|
std::vector<CutIsland> cut_islands;
|
||||||
|
Loading…
Reference in New Issue
Block a user