Merged with master

This commit is contained in:
Vojtech Bubnik 2022-11-07 14:51:07 +01:00
parent 8858651bf4
commit 3cdacd700c
12 changed files with 276 additions and 120 deletions

View File

@ -1502,27 +1502,27 @@ void GCode::process_layers(
} }
}); });
const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order, const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
[&spiral_vase = *m_spiral_vase](LayerResult in) -> LayerResult { [spiral_vase = this->m_spiral_vase.get()](LayerResult in) -> LayerResult {
if (in.nop_layer_result) if (in.nop_layer_result)
return in; return in;
spiral_vase.enable(in.spiral_vase_enable); spiral_vase->enable(in.spiral_vase_enable);
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
}); });
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order, const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
[&pressure_equalizer = *m_pressure_equalizer](LayerResult in) -> LayerResult { [pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
return pressure_equalizer.process_layer(std::move(in)); return pressure_equalizer->process_layer(std::move(in));
}); });
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order, const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
[&cooling_buffer = *m_cooling_buffer](LayerResult in) -> std::string { [cooling_buffer = this->m_cooling_buffer.get()](LayerResult in) -> std::string {
if (in.nop_layer_result) if (in.nop_layer_result)
return in.gcode; return in.gcode;
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); return cooling_buffer->process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
}); });
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order, const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
[&self = *m_find_replace](std::string s) -> std::string { [find_replace = this->m_find_replace.get()](std::string s) -> std::string {
return self.process_layer(std::move(s)); return find_replace->process_layer(std::move(s));
}); });
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order, const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
[&output_stream](std::string s) { output_stream.write(s); } [&output_stream](std::string s) { output_stream.write(s); }
@ -1584,25 +1584,25 @@ void GCode::process_layers(
} }
}); });
const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order, const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
[&spiral_vase = *m_spiral_vase](LayerResult in)->LayerResult { [spiral_vase = this->m_spiral_vase.get()](LayerResult in)->LayerResult {
if (in.nop_layer_result) if (in.nop_layer_result)
return in; return in;
spiral_vase.enable(in.spiral_vase_enable); spiral_vase->enable(in.spiral_vase_enable);
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
}); });
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order, const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
[&pressure_equalizer = *m_pressure_equalizer](LayerResult in) -> LayerResult { [pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
return pressure_equalizer.process_layer(std::move(in)); return pressure_equalizer->process_layer(std::move(in));
}); });
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order, const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
[&cooling_buffer = *m_cooling_buffer](LayerResult in)->std::string { [cooling_buffer = this->m_cooling_buffer.get()](LayerResult in)->std::string {
if (in.nop_layer_result) if (in.nop_layer_result)
return in.gcode; return in.gcode;
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); return cooling_buffer->process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
}); });
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order, const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
[&self = *m_find_replace](std::string s) -> std::string { [find_replace = this->m_find_replace.get()](std::string s) -> std::string {
return self.process_layer(std::move(s)); return find_replace->process_layer(std::move(s));
}); });
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order, const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
[&output_stream](std::string s) { output_stream.write(s); } [&output_stream](std::string s) { output_stream.write(s); }

View File

@ -87,6 +87,10 @@ void MeasuringImpl::update_planes()
return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001);
}; };
// First go through all the triangles and fill in m_planes vector. For each "plane"
// detected on the model, it will contain list of facets that are part of it.
// We will also fill in m_face_to_plane, which contains index into m_planes
// for each of the source facets.
while (1) { while (1) {
// Find next unvisited triangle: // Find next unvisited triangle:
for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx)
@ -117,20 +121,29 @@ void MeasuringImpl::update_planes()
m_planes.back().normal = normal_ptr->cast<double>(); m_planes.back().normal = normal_ptr->cast<double>();
std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end());
} }
// Check that each facet is part of one of the planes.
assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); }));
// Now we will walk around each of the planes and save vertices which form the border.
SurfaceMesh sm(m_its); SurfaceMesh sm(m_its);
for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) {
//int plane_id = 5; {
const auto& facets = m_planes[plane_id].facets; const auto& facets = m_planes[plane_id].facets;
m_planes[plane_id].borders.clear(); m_planes[plane_id].borders.clear();
std::vector<std::array<bool, 3>> visited(facets.size(), {false, false, false}); std::vector<std::array<bool, 3>> visited(facets.size(), {false, false, false});
for (int face_id=0; face_id<int(facets.size()); ++face_id) { for (int face_id=0; face_id<int(facets.size()); ++face_id) {
assert(m_face_to_plane[facets[face_id]] == plane_id); assert(m_face_to_plane[facets[face_id]] == plane_id);
for (int edge_id=0; edge_id<3; ++edge_id) { for (int edge_id=0; edge_id<3; ++edge_id) {
if (visited[face_id][edge_id] || (int)m_face_to_plane[face_neighbors[facets[face_id]][edge_id]] == plane_id) { // Every facet's edge which has a neighbor from a different plane is
// part of an edge that we want to walk around. Skip the others.
int neighbor_idx = face_neighbors[facets[face_id]][edge_id];
if (neighbor_idx == -1)
goto PLANE_FAILURE;
if (visited[face_id][edge_id] || (int)m_face_to_plane[neighbor_idx] == plane_id) {
visited[face_id][edge_id] = true; visited[face_id][edge_id] = true;
continue; continue;
} }
@ -156,14 +169,22 @@ void MeasuringImpl::update_planes()
do { do {
const Halfedge_index he_orig = he; const Halfedge_index he_orig = he;
he = sm.next_around_target(he); he = sm.next_around_target(he);
while ( (int)m_face_to_plane[sm.face(he)] == plane_id && he != he_orig) if (he.is_invalid())
goto PLANE_FAILURE;
while ( (int)m_face_to_plane[sm.face(he)] == plane_id && he != he_orig) {
he = sm.next_around_target(he); he = sm.next_around_target(he);
if (he.is_invalid())
goto PLANE_FAILURE;
}
he = sm.opposite(he); he = sm.opposite(he);
if (he.is_invalid())
goto PLANE_FAILURE;
Face_index fi = he.face(); Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
assert(face_it != facets.end()); if (face_it == facets.end() || *face_it != int(fi)) // This indicates a broken mesh.
assert(*face_it == int(fi)); goto PLANE_FAILURE;
if (visited[face_it - facets.begin()][he.side()] && he != he_start) { if (visited[face_it - facets.begin()][he.side()] && he != he_start) {
last_border.resize(1); last_border.resize(1);
break; break;
@ -175,13 +196,16 @@ void MeasuringImpl::update_planes()
if (last_border.size() == 1) if (last_border.size() == 1)
m_planes[plane_id].borders.pop_back(); m_planes[plane_id].borders.pop_back();
else {
assert(last_border.front() == last_border.back());
}
} }
} }
} continue; // There was no failure.
m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), PLANE_FAILURE:
[](const PlaneData& p) { return p.borders.empty(); }), m_planes[plane_id].borders.clear();
m_planes.end()); }
} }
@ -191,9 +215,6 @@ void MeasuringImpl::update_planes()
void MeasuringImpl::extract_features() void MeasuringImpl::extract_features()
{ {
auto N_to_angle = [](double N) -> double { return 2.*M_PI / N; };
constexpr double polygon_upper_threshold = N_to_angle(4.5);
constexpr double polygon_lower_threshold = N_to_angle(8.5);
std::vector<double> angles; std::vector<double> angles;
std::vector<double> lengths; std::vector<double> lengths;
@ -209,31 +230,39 @@ void MeasuringImpl::extract_features()
trafo.rotate(q); trafo.rotate(q);
for (const std::vector<Vec3d>& border : plane.borders) { for (const std::vector<Vec3d>& border : plane.borders) {
assert(border.size() > 1); if (border.size() <= 1)
continue;
assert(border.front() == border.back());
int start_idx = -1; int start_idx = -1;
std::vector<SurfaceFeature> edges;
// First calculate angles at all the vertices. // First calculate angles at all the vertices.
angles.clear(); angles.clear();
lengths.clear(); lengths.clear();
for (int i=0; i<int(border.size()); ++i) { for (int i=0; i<int(border.size()); ++i) { // front is the same as back, hence the weird indexing
const Vec3d& v2 = (i == 0 ? border[0] - border[border.size()-1] const Vec3d& v2 = (i == 0 ? border[0] - border[border.size()-2]
: border[i] - border[i-1]); : border[i] - border[i-1]);
const Vec3d& v1 = i == (int)border.size()-1 ? border[0] - border.back() const Vec3d& v1 = i == (int)border.size()-1 ? border[1] - border.back()
: border[i+1] - border[i]; : border[i+1] - border[i];
double angle = atan2(-normal.dot(v1.cross(v2)), -v1.dot(v2)) + M_PI; double angle = atan2(-normal.dot(v1.cross(v2)), -v1.dot(v2)) + M_PI;
if (angle > M_PI) if (angle > M_PI)
angle = 2*M_PI - angle; angle = 2*M_PI - angle;
angles.push_back(angle); angles.push_back(angle);
lengths.push_back(v2.squaredNorm()); lengths.push_back(v2.norm());
} }
assert(border.size() == angles.size()); assert(border.size() == angles.size());
assert(border.size() == lengths.size()); assert(border.size() == lengths.size());
// First go around the border and pick what might be circular segments.
// Save pair of indices to where such potential segments start and end.
// Also remember the length of these segments.
bool circle = false; bool circle = false;
std::vector<SurfaceFeature> circles; std::vector<SurfaceFeature> circles;
std::vector<std::pair<size_t, size_t>> circles_idxs; std::vector<std::pair<int, int>> circles_idxs;
std::vector<double> circles_lengths;
for (int i=1; i<(int)angles.size(); ++i) { for (int i=1; i<(int)angles.size(); ++i) {
if (Slic3r::is_approx(lengths[i], lengths[i-1]) if (Slic3r::is_approx(lengths[i], lengths[i-1])
&& Slic3r::is_approx(angles[i], angles[i-1]) && Slic3r::is_approx(angles[i], angles[i-1])
@ -245,62 +274,149 @@ void MeasuringImpl::extract_features()
} }
} else { } else {
if (circle) { if (circle) {
// Add the circle and remember indices into borders.
const auto& [center, radius] = get_center_and_radius(border, start_idx, i, trafo); const auto& [center, radius] = get_center_and_radius(border, start_idx, i, trafo);
// Add the circle and remember indices into borders.
circles_idxs.emplace_back(start_idx, i); circles_idxs.emplace_back(start_idx, i);
circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius)); circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius));
circles_lengths.emplace_back(std::accumulate(lengths.begin() + start_idx + 1, lengths.begin() + i + 1, 0.));
circle = false; circle = false;
} }
} }
} }
// Some of the "circles" may actually be polygons. We want them detected as // At this point we might need to merge the first and last segment, if the starting
// edges, but also to remember the center and save it into those edges. // point happened to be inside the segment. The discrimination of too small segments
// We will add all such edges manually and delete the detected circles, // will follow, so we need a complete picture before that.
// leaving it in circles_idxs so they are not picked again: if (circles_idxs.size() > 1
&& circles_idxs.back().second == angles.size()-1
&& circles_idxs.front().first == 0) {
// Possibly the same circle. Check that the angle and length criterion holds along the combined segment.
bool same = true;
double last_len = -1.;
double last_angle = 0.;
for (int i=circles_idxs.back().first + 1; i != circles_idxs.front().second; ++i) {
if (i == angles.size())
i = 1;
if (last_len == -1.) {
last_len = lengths[i];
last_angle = angles[i];
} else {
if (! Slic3r::is_approx(lengths[i], last_len) || ! Slic3r::is_approx(angles[i], last_angle)) {
same = false;
break;
}
}
}
if (same) {
// This seems to really be the same circle. Better apply ransac again. The parts can be small and inexact.
std::vector<Vec3d> points(border.begin() + circles_idxs.back().first, border.end());
points.insert(points.end(), border.begin(), border.begin() + circles_idxs.front().second+1);
auto [c, radius] = get_center_and_radius(points, 0, points.size()-1, trafo);
// Now replace the first circle with the combined one, remove the last circle.
// First index of the first circle is saved negative - we are going to pick edges
// from the border later, we will need to know where the merged in segment was.
// The sign simplifies the algorithm that picks the remaining edges - see below.
circles.front() = SurfaceFeature(SurfaceFeatureType::Circle, c, plane.normal, std::nullopt, radius);
circles_idxs.front().first = - circles_idxs.back().first;
circles_lengths.front() += circles_lengths.back();
circles.pop_back();
circles_idxs.pop_back();
circles_lengths.pop_back();
}
}
// Now throw away all circles that subtend less than 90 deg.
assert(circles.size() == circles_lengths.size());
for (int i=0; i<int(circles.size()); ++i) {
double r = std::get<1>(circles[i].get_circle());
if (circles_lengths[i] / r < 0.9*M_PI/2.) {
circles_lengths.erase(circles_lengths.begin() + i);
circles.erase(circles.begin() + i);
circles_idxs.erase(circles_idxs.begin() + i);
--i;
}
}
circles_lengths.clear(); // no longer needed, make it obvious
// Some of the "circles" may actually be polygons (5-8 vertices). We want them
// detected as edges, but also to remember the center and save it into those edges.
// We will add all such edges manually and delete the detected circles, leaving it
// in circles_idxs so they are not picked again.
assert(circles.size() == circles_idxs.size()); assert(circles.size() == circles_idxs.size());
for (int i=circles.size()-1; i>=0; --i) { for (int i=circles.size()-1; i>=0; --i) {
assert(circles_idxs[i].first + 1 < angles.size() - 1); // Check that this is internal point of the circle, not the first, not the last. if (circles_idxs[i].first == 0 && circles_idxs[i].second == border.size()-1) {
double angle = angles[circles_idxs[i].first + 1]; int N = circles_idxs[i].second - circles_idxs[i].first;
if (angle > polygon_lower_threshold) { if (N <= 8) {
if (angle < polygon_upper_threshold) { if (N >= 5) { // polygon = 5,6,7,8 vertices
const Vec3d center = std::get<0>(circles[i].get_circle()); const Vec3d center = std::get<0>(circles[i].get_circle());
for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j)
plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
border[j - 1], border[j], std::make_optional(center))); border[j - 1], border[j], std::make_optional(center)));
} else { } else {
// This will be handled just like a regular edge. // This will be handled just like a regular edge (squares, triangles).
circles_idxs.erase(circles_idxs.begin() + i); circles_idxs.erase(circles_idxs.begin() + i);
}
circles.erase(circles.begin() + i);
} }
}
}
// Anything under 5 vertices shall not be considered a circle.
assert(circles_idxs.size() == circles.size());
for (int i=0; i<int(circles_idxs.size()); ++i) {
const auto& [start, end] = circles_idxs[i];
int N = start >= 0
? end - start + (start == 0 && end == border.size()-1 ? 0 : 1) // last point is the same as first
: end + (border.size() + start);
if (N < 5) {
circles.erase(circles.begin() + i); circles.erase(circles.begin() + i);
circles_idxs.erase(circles_idxs.begin() + i);
--i;
} }
} }
// We have the circles. Now go around again and pick edges, while jumping over circles.
// If the first index of the first circle is negative, it means that it was merged
// with a segment that was originally at the back and is no longer there. Ressurect
// its pair of indices so that edges are not picked again.
// We have the circles. Now go around again and pick edges. if (! circles_idxs.empty() && circles_idxs.front().first < 0)
int cidx = 0; // index of next circle in the way circles_idxs.emplace_back(-circles_idxs.front().first, int(border.size()));
int cidx = 0; // index of next circle to jump over
for (int i=1; i<int(border.size()); ++i) { for (int i=1; i<int(border.size()); ++i) {
if (cidx < (int)circles_idxs.size() && i > (int)circles_idxs[cidx].first) if (cidx < (int)circles_idxs.size() && i > (int)circles_idxs[cidx].first)
i = circles_idxs[cidx++].second; i = circles_idxs[cidx++].second;
else else
plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i - 1], border[i])); edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i - 1], border[i]));
} }
// FIXME Throw away / do not create edges which are parts of circles or // Merge adjacent edges where needed.
// which lead to circle points (unless they belong to the same plane.) assert(std::all_of(edges.begin(), edges.end(),
[](const SurfaceFeature& f) { return f.get_type() == SurfaceFeatureType::Edge; }));
for (int i=edges.size()-1; i>=0; --i) {
const auto& [first_start, first_end] = edges[i==0 ? edges.size()-1 : i-1].get_edge();
const auto& [second_start, second_end] = edges[i].get_edge();
// FIXME Check and merge first and last circle if needed. if (Slic3r::is_approx(first_end, second_start)
&& Slic3r::is_approx((first_end-first_start).normalized().dot((second_end-second_start).normalized()), 1.)) {
// The edges have the same direction and share a point. Merge them.
edges[i==0 ? edges.size()-1 : i-1] = SurfaceFeature(SurfaceFeatureType::Edge, first_start, second_end);
edges.erase(edges.begin() + i);
}
}
// Now move the circles into the feature list. // Now move the circles and edges into the feature list for the plane.
assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) { assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) {
return f.get_type() == SurfaceFeatureType::Circle; return f.get_type() == SurfaceFeatureType::Circle;
})); }));
assert(std::all_of(edges.begin(), edges.end(), [](const SurfaceFeature& f) {
return f.get_type() == SurfaceFeatureType::Edge;
}));
plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()),
std::make_move_iterator(circles.end())); std::make_move_iterator(circles.end()));
plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(edges.begin()),
std::make_move_iterator(edges.end()));
} }
// The last surface feature is the plane itself. // The last surface feature is the plane itself.
@ -607,8 +723,8 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (f2.get_type() == SurfaceFeatureType::Point) { if (f2.get_type() == SurfaceFeatureType::Point) {
Vec3d diff = (f2.get_point() - f1.get_point()); Vec3d diff = (f2.get_point() - f1.get_point());
result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()});
result.distance_xyz = diff; result.distance_xyz = diff.cwiseAbs();
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Edge) { } else if (f2.get_type() == SurfaceFeatureType::Edge) {
const auto [s,e] = f2.get_edge(); const auto [s,e] = f2.get_edge();
@ -659,16 +775,16 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (f2.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) {
std::vector<DistAndPoints> distances; std::vector<DistAndPoints> distances;
// auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair<Vec3d, Vec3d>& e) { auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair<Vec3d, Vec3d>& e) {
// const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second));
// double distance = res.distance_strict->dist; double distance = res.distance_strict->dist;
// Vec3d v2 = res.distance_strict->to; Vec3d v2 = res.distance_strict->to;
//
// const Vec3d e1e2 = e.second - e.first; const Vec3d e1e2 = e.second - e.first;
// const Vec3d e1v2 = v2 - e.first; const Vec3d e1v2 = v2 - e.first;
// if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm())
// distances.emplace_back(distance, v, v2); distances.emplace_back(distance, v, v2);
// }; };
std::pair<Vec3d, Vec3d> e1 = f1.get_edge(); std::pair<Vec3d, Vec3d> e1 = f1.get_edge();
std::pair<Vec3d, Vec3d> e2 = f2.get_edge(); std::pair<Vec3d, Vec3d> e2 = f2.get_edge();
@ -677,10 +793,10 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second);
distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first);
distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second);
// add_point_edge_distance(e1.first, e2); add_point_edge_distance(e1.first, e2);
// add_point_edge_distance(e1.second, e2); add_point_edge_distance(e1.second, e2);
// add_point_edge_distance(e2.first, e1); add_point_edge_distance(e2.first, e1);
// add_point_edge_distance(e2.second, e1); add_point_edge_distance(e2.second, e1);
auto it = std::min_element(distances.begin(), distances.end(), auto it = std::min_element(distances.begin(), distances.end(),
[](const DistAndPoints& item1, const DistAndPoints& item2) { [](const DistAndPoints& item1, const DistAndPoints& item2) {
return item1.dist < item2.dist; return item1.dist < item2.dist;
@ -1037,14 +1153,6 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
else else
result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane());
} }
// validation
if (result.distance_infinite.has_value() && result.distance_infinite->dist < EPSILON)
result.distance_infinite.reset();
if (result.distance_strict.has_value() && result.distance_strict->dist < EPSILON)
result.distance_strict.reset();
if (result.angle.has_value() && std::abs(result.angle->angle) < EPSILON)
result.angle.reset();
return result; return result;
} }

View File

@ -150,6 +150,17 @@ struct MeasurementResult {
bool has_any_data() const { bool has_any_data() const {
return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value();
} }
void transform(const Transform3d& trafo) {
if (angle.has_value())
angle->transform(trafo);
if (distance_infinite.has_value())
distance_infinite->transform(trafo);
if (distance_strict.has_value()) {
distance_strict->transform(trafo);
distance_xyz = (distance_strict->to - distance_strict->from).cwiseAbs();
}
}
}; };
// Returns distance/angle between two SurfaceFeatures. // Returns distance/angle between two SurfaceFeatures.

View File

@ -223,7 +223,10 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c
#if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL
m_contour = ExPolygon(Polygon::new_scale(bed_shape)); m_contour = ExPolygon(Polygon::new_scale(bed_shape));
m_polygon = offset(m_contour.contour, (float)m_contour.contour.bounding_box().radius() * 1.7f, jtRound, scale_(0.5)).front(); const BoundingBox bbox = m_contour.contour.bounding_box();
if (!bbox.defined)
throw RuntimeError(std::string("Invalid bed shape"));
m_polygon = offset(m_contour.contour, (float)bbox.radius() * 1.7f, jtRound, scale_(0.5)).front();
m_triangles.reset(); m_triangles.reset();
m_gridlines.reset(); m_gridlines.reset();

View File

@ -3460,6 +3460,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports &&
m_gizmos.get_current_type() != GLGizmosManager::Seam && m_gizmos.get_current_type() != GLGizmosManager::Seam &&
m_gizmos.get_current_type() != GLGizmosManager::Cut && m_gizmos.get_current_type() != GLGizmosManager::Cut &&
m_gizmos.get_current_type() != GLGizmosManager::Measure &&
m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) {
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
m_dirty = true; m_dirty = true;
@ -3695,7 +3696,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// if right clicking on volume, propagate event through callback (shows context menu) // if right clicking on volume, propagate event through callback (shows context menu)
int volume_idx = get_first_hover_volume_idx(); int volume_idx = get_first_hover_volume_idx();
if (!m_volumes.volumes[volume_idx]->is_wipe_tower // no context menu for the wipe tower if (!m_volumes.volumes[volume_idx]->is_wipe_tower // no context menu for the wipe tower
&& m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) // disable context menu when the gizmo is open && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && m_gizmos.get_current_type() != GLGizmosManager::Measure)) // disable context menu when the gizmo is open
{ {
// forces the selection of the volume // forces the selection of the volume
/* m_selection.add(volume_idx); // #et_FIXME_if_needed /* m_selection.add(volume_idx); // #et_FIXME_if_needed
@ -3719,7 +3720,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (!m_mouse.dragging) { if (!m_mouse.dragging) {
// do not post the event if the user is panning the scene // do not post the event if the user is panning the scene
// or if right click was done over the wipe tower // or if right click was done over the wipe tower
const bool post_right_click_event = m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower; const bool post_right_click_event = (m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower) &&
m_gizmos.get_current_type() != GLGizmosManager::Measure;
if (post_right_click_event) if (post_right_click_event)
post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() })); post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() }));
} }

View File

@ -2882,6 +2882,9 @@ static bool can_add_volumes_to_object(const ModelObject* object)
wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/) wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
{ {
const bool is_prevent_list_events = m_prevent_list_events;
m_prevent_list_events = true;
wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx)); wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx));
m_objects_model->DeleteVolumeChildren(object_item); m_objects_model->DeleteVolumeChildren(object_item);
@ -2909,6 +2912,7 @@ wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, st
Expand(object_item); Expand(object_item);
} }
m_prevent_list_events = is_prevent_list_events;
return items; return items;
} }

View File

@ -303,16 +303,11 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) { if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) {
m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get()); m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get());
// transform to world coordinates // transform to world coordinates
if (m_measurement_result.angle.has_value()) m_measurement_result.transform(m_volume_matrix);
m_measurement_result.angle->transform(m_volume_matrix);
if (m_measurement_result.distance_infinite.has_value())
m_measurement_result.distance_infinite->transform(m_volume_matrix);
if (m_measurement_result.distance_strict.has_value())
m_measurement_result.distance_strict->transform(m_volume_matrix);
if (m_measurement_result.distance_xyz.has_value())
m_measurement_result.distance_xyz = TransformHelper::model_to_world(*m_measurement_result.distance_xyz, m_volume_matrix);
} }
m_imgui->set_requires_extra_frame();
return true; return true;
} }
@ -334,7 +329,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
else if (mouse_event.RightDown() && mouse_event.CmdDown()) { else if (mouse_event.RightDown() && mouse_event.CmdDown()) {
m_selected_features.reset(); m_selected_features.reset();
m_selection_raycasters.clear(); m_selection_raycasters.clear();
m_imgui->set_requires_extra_frame(); m_parent.request_extra_frame();
} }
else if (mouse_event.Leaving()) else if (mouse_event.Leaving())
m_mouse_left_down = false; m_mouse_left_down = false;
@ -357,7 +352,14 @@ void GLGizmoMeasure::data_changed()
m_last_inv_zoom = 0.0f; m_last_inv_zoom = 0.0f;
m_last_plane_idx = -1; m_last_plane_idx = -1;
m_selected_features.reset(); if (m_pending_scale) {
m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get());
// transform to world coordinates
m_measurement_result.transform(m_volume_matrix);
m_pending_scale = false;
}
else
m_selected_features.reset();
m_selection_raycasters.clear(); m_selection_raycasters.clear();
m_editing_distance = false; m_editing_distance = false;
m_is_editing_distance_first_frame = true; m_is_editing_distance_first_frame = true;
@ -905,7 +907,7 @@ void GLGizmoMeasure::render_dimensioning()
return; return;
auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2, float distance) { auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2, float distance) {
if (v1.isApprox(v2)) if ((v2 - v1).squaredNorm() < 0.000001 || distance < 0.001f)
return; return;
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
@ -1007,6 +1009,9 @@ void GLGizmoMeasure::render_dimensioning()
selection.scale(ratio * Vec3d::Ones(), type); selection.scale(ratio * Vec3d::Ones(), type);
wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot
wxGetApp().obj_manipul()->set_dirty(); wxGetApp().obj_manipul()->set_dirty();
// update measure on next call to data_changed()
m_pending_scale = true;
}; };
auto action_exit = [this]() { auto action_exit = [this]() {
m_editing_distance = false; m_editing_distance = false;
@ -1271,13 +1276,6 @@ void GLGizmoMeasure::render_dimensioning()
if (m_selected_features.second.feature.has_value()) { if (m_selected_features.second.feature.has_value()) {
const bool has_distance = m_measurement_result.has_distance_data(); const bool has_distance = m_measurement_result.has_distance_data();
if (has_distance) {
// Render the arrow between the points that the backend passed:
const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value()
? *m_measurement_result.distance_infinite
: *m_measurement_result.distance_strict;
point_point(dap.from, dap.to, dap.dist);
}
const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature);
const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature);
@ -1290,17 +1288,25 @@ void GLGizmoMeasure::render_dimensioning()
std::swap(f1, f2); std::swap(f1, f2);
} }
// Where needed, draw also the extension of the edge to where the dist is measured: // If there is an angle to show, draw the arc:
if (has_distance && ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge)
point_edge(*f1, *f2);
// Now if there is an angle to show, draw the arc:
if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge)
arc_edge_edge(*f1, *f2); arc_edge_edge(*f1, *f2);
else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane)
arc_edge_plane(*f1, *f2); arc_edge_plane(*f1, *f2);
else if (ft1 == Measure::SurfaceFeatureType::Plane && ft2 == Measure::SurfaceFeatureType::Plane) else if (ft1 == Measure::SurfaceFeatureType::Plane && ft2 == Measure::SurfaceFeatureType::Plane)
arc_plane_plane(*f1, *f2); arc_plane_plane(*f1, *f2);
if (has_distance){
// Where needed, draw the extension of the edge to where the dist is measured:
if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge)
point_edge(*f1, *f2);
// Render the arrow between the points that the backend passed:
const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value()
? *m_measurement_result.distance_infinite
: *m_measurement_result.distance_strict;
point_point(dap.from, dap.to, dap.dist);
}
} }
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));

View File

@ -116,6 +116,7 @@ class GLGizmoMeasure : public GLGizmoBase
KeyAutoRepeatFilter m_ctrl_kar_filter; KeyAutoRepeatFilter m_ctrl_kar_filter;
SelectedFeatures m_selected_features; SelectedFeatures m_selected_features;
bool m_pending_scale{ false };
bool m_editing_distance{ false }; bool m_editing_distance{ false };
bool m_is_editing_distance_first_frame{ true }; bool m_is_editing_distance_first_frame{ true };

View File

@ -538,7 +538,7 @@ void MainFrame::update_layout()
case ESettingsLayout::GCodeViewer: case ESettingsLayout::GCodeViewer:
{ {
m_main_sizer->Add(m_plater, 1, wxEXPAND); m_main_sizer->Add(m_plater, 1, wxEXPAND);
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true); m_plater->set_default_bed_shape();
m_plater->get_collapse_toolbar().set_enabled(false); m_plater->get_collapse_toolbar().set_enabled(false);
m_plater->collapse_sidebar(true); m_plater->collapse_sidebar(true);
m_plater->Show(); m_plater->Show();

View File

@ -5477,10 +5477,27 @@ void Plater::load_gcode(const wxString& filename)
p->gcode_result = std::move(processor.extract_result()); p->gcode_result = std::move(processor.extract_result());
// show results // show results
p->preview->reload_print(false); try
{
p->preview->reload_print(false);
}
catch (const std::exception&)
{
wxEndBusyCursor();
p->gcode_result.reset();
reset_gcode_toolpaths();
set_default_bed_shape();
p->preview->reload_print(false);
p->get_current_canvas3D()->render();
MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
wxString(GCODEVIEWER_APP_NAME) + " - " + _L("Error while loading .gcode file"), wxOK | wxICON_WARNING | wxCENTRE).ShowModal();
set_project_filename(wxEmptyString);
return;
}
p->preview->get_canvas3d()->zoom_to_gcode(); p->preview->get_canvas3d()->zoom_to_gcode();
if (p->preview->get_canvas3d()->get_gcode_layers_zs().empty()) { if (p->preview->get_canvas3d()->get_gcode_layers_zs().empty()) {
wxEndBusyCursor();
//wxMessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."), //wxMessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."), MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
wxString(GCODEVIEWER_APP_NAME) + " - " + _L("Error while loading .gcode file"), wxOK | wxICON_WARNING | wxCENTRE).ShowModal(); wxString(GCODEVIEWER_APP_NAME) + " - " + _L("Error while loading .gcode file"), wxOK | wxICON_WARNING | wxCENTRE).ShowModal();
@ -6646,6 +6663,11 @@ void Plater::set_bed_shape(const Pointfs& shape, const double max_print_height,
p->set_bed_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom); p->set_bed_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom);
} }
void Plater::set_default_bed_shape() const
{
set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true);
}
void Plater::force_filament_colors_update() void Plater::force_filament_colors_update()
{ {
bool update_scheduled = false; bool update_scheduled = false;

View File

@ -399,6 +399,7 @@ public:
void set_bed_shape() const; void set_bed_shape() const;
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const; void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
void set_default_bed_shape() const;
NotificationManager * get_notification_manager(); NotificationManager * get_notification_manager();
const NotificationManager * get_notification_manager() const; const NotificationManager * get_notification_manager() const;

View File

@ -594,8 +594,6 @@ void SearchDialog::ProcessSelection(wxDataViewItem selection)
void SearchDialog::OnInputText(wxCommandEvent&) void SearchDialog::OnInputText(wxCommandEvent&)
{ {
search_line->SetInsertionPointEnd();
wxString input_string = search_line->GetValue(); wxString input_string = search_line->GetValue();
if (input_string == default_string) if (input_string == default_string)
input_string.Clear(); input_string.Clear();