mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-09-26 13:03:13 +08:00
Merge branch 'lh_mm_segmentation_volumes'
This commit is contained in:
commit
1422df61ec
@ -2588,11 +2588,9 @@ namespace Slic3r {
|
|||||||
assert(index < geometry.custom_supports.size());
|
assert(index < geometry.custom_supports.size());
|
||||||
assert(index < geometry.custom_seam.size());
|
assert(index < geometry.custom_seam.size());
|
||||||
assert(index < geometry.mm_segmentation.size());
|
assert(index < geometry.mm_segmentation.size());
|
||||||
if (! geometry.custom_supports[index].empty())
|
|
||||||
volume->supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]);
|
volume->supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]);
|
||||||
if (! geometry.custom_seam[index].empty())
|
|
||||||
volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]);
|
volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]);
|
||||||
if (! geometry.mm_segmentation[index].empty())
|
|
||||||
volume->mm_segmentation_facets.set_triangle_from_string(i, geometry.mm_segmentation[index]);
|
volume->mm_segmentation_facets.set_triangle_from_string(i, geometry.mm_segmentation[index]);
|
||||||
}
|
}
|
||||||
volume->supported_facets.shrink_to_fit();
|
volume->supported_facets.shrink_to_fit();
|
||||||
|
@ -58,10 +58,7 @@ std::pair<std::vector<Vec2d>, std::vector<PointType>> remove_redundant_points(
|
|||||||
const std::int64_t index{std::distance(points.begin(), iterator)};
|
const std::int64_t index{std::distance(points.begin(), iterator)};
|
||||||
if (next(iterator) == points.end() || point_types[index] != point_types[index + 1]) {
|
if (next(iterator) == points.end() || point_types[index] != point_types[index + 1]) {
|
||||||
std::vector<Vec2d> simplification_result;
|
std::vector<Vec2d> simplification_result;
|
||||||
douglas_peucker<double>(
|
douglas_peucker(range_start, next(iterator), std::back_inserter(simplification_result), tolerance);
|
||||||
range_start, next(iterator), std::back_inserter(simplification_result), tolerance,
|
|
||||||
[](const Vec2d &point) { return point; }
|
|
||||||
);
|
|
||||||
|
|
||||||
points_result.insert(
|
points_result.insert(
|
||||||
points_result.end(), simplification_result.begin(), simplification_result.end()
|
points_result.end(), simplification_result.begin(), simplification_result.end()
|
||||||
|
@ -190,6 +190,10 @@ template<class L> bool intersection(const L &l1, const L &l2, Vec<Dim<L>, Scalar
|
|||||||
return false; // not intersecting
|
return false; // not intersecting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Point midpoint(const Point &a, const Point &b) {
|
||||||
|
return (a + b) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace line_alg
|
} // namespace line_alg
|
||||||
|
|
||||||
class Line
|
class Line
|
||||||
@ -204,7 +208,7 @@ public:
|
|||||||
void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
|
void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
|
||||||
void reverse() { std::swap(this->a, this->b); }
|
void reverse() { std::swap(this->a, this->b); }
|
||||||
double length() const { return (b.cast<double>() - a.cast<double>()).norm(); }
|
double length() const { return (b.cast<double>() - a.cast<double>()).norm(); }
|
||||||
Point midpoint() const { return (this->a + this->b) / 2; }
|
Point midpoint() const { return line_alg::midpoint(this->a, this->b); }
|
||||||
bool intersection_infinite(const Line &other, Point* point) const;
|
bool intersection_infinite(const Line &other, Point* point) const;
|
||||||
bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; }
|
bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; }
|
||||||
double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); }
|
double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); }
|
||||||
|
@ -2043,35 +2043,46 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
|
|||||||
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
|
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, TriangleStateType type) const
|
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume &mv, TriangleStateType type) const {
|
||||||
{
|
|
||||||
TriangleSelector selector(mv.mesh());
|
TriangleSelector selector(mv.mesh());
|
||||||
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||||
selector.deserialize(m_data, false);
|
selector.deserialize(m_data, false);
|
||||||
return selector.get_facets(type);
|
return selector.get_facets(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, TriangleStateType type) const
|
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume &mv, TriangleStateType type) const {
|
||||||
{
|
|
||||||
TriangleSelector selector(mv.mesh());
|
TriangleSelector selector(mv.mesh());
|
||||||
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||||
selector.deserialize(m_data, false);
|
selector.deserialize(m_data, false);
|
||||||
return selector.get_facets_strict(type);
|
return selector.get_facets_strict(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FacetsAnnotation::has_facets(const ModelVolume& mv, TriangleStateType type) const
|
indexed_triangle_set_with_color FacetsAnnotation::get_all_facets_with_colors(const ModelVolume &mv) const {
|
||||||
{
|
TriangleSelector selector(mv.mesh());
|
||||||
|
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||||
|
selector.deserialize(m_data, false);
|
||||||
|
return selector.get_all_facets_with_colors();
|
||||||
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set_with_color FacetsAnnotation::get_all_facets_strict_with_colors(const ModelVolume &mv) const {
|
||||||
|
TriangleSelector selector(mv.mesh());
|
||||||
|
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||||
|
selector.deserialize(m_data, false);
|
||||||
|
return selector.get_all_facets_strict_with_colors();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FacetsAnnotation::has_facets(const ModelVolume &mv, TriangleStateType type) const {
|
||||||
return TriangleSelector::has_facets(m_data, type);
|
return TriangleSelector::has_facets(m_data, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
bool FacetsAnnotation::set(const TriangleSelector &selector) {
|
||||||
{
|
|
||||||
TriangleSelector::TriangleSplittingData sel_map = selector.serialize();
|
TriangleSelector::TriangleSplittingData sel_map = selector.serialize();
|
||||||
if (sel_map != m_data) {
|
if (sel_map != m_data) {
|
||||||
m_data = std::move(sel_map);
|
m_data = std::move(sel_map);
|
||||||
this->touch();
|
this->touch();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2113,6 +2124,12 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
|||||||
// generated by get_triangle_as_string. Used to load from 3MF.
|
// generated by get_triangle_as_string. Used to load from 3MF.
|
||||||
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string &str)
|
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string &str)
|
||||||
{
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
// The triangle isn't painted, so it means that it will use the default extruder.
|
||||||
|
m_data.used_states[static_cast<int>(TriangleStateType::NONE)] = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(!str.empty());
|
assert(!str.empty());
|
||||||
assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id);
|
assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id);
|
||||||
m_data.triangles_to_split.emplace_back(triangle_id, int(m_data.bitstream.size()));
|
m_data.triangles_to_split.emplace_back(triangle_id, int(m_data.bitstream.size()));
|
||||||
|
@ -670,6 +670,8 @@ public:
|
|||||||
bool set(const TriangleSelector &selector);
|
bool set(const TriangleSelector &selector);
|
||||||
indexed_triangle_set get_facets(const ModelVolume &mv, TriangleStateType type) const;
|
indexed_triangle_set get_facets(const ModelVolume &mv, TriangleStateType type) const;
|
||||||
indexed_triangle_set get_facets_strict(const ModelVolume &mv, TriangleStateType type) const;
|
indexed_triangle_set get_facets_strict(const ModelVolume &mv, TriangleStateType type) const;
|
||||||
|
indexed_triangle_set_with_color get_all_facets_with_colors(const ModelVolume &mv) const;
|
||||||
|
indexed_triangle_set_with_color get_all_facets_strict_with_colors(const ModelVolume &mv) const;
|
||||||
bool has_facets(const ModelVolume &mv, TriangleStateType type) const;
|
bool has_facets(const ModelVolume &mv, TriangleStateType type) const;
|
||||||
bool empty() const { return m_data.triangles_to_split.empty(); }
|
bool empty() const { return m_data.triangles_to_split.empty(); }
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,8 @@ using ColoredLines = std::vector<ColoredLine>;
|
|||||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
||||||
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||||
|
|
||||||
|
BoundingBox get_extents(const std::vector<ColoredLines> &colored_polygons);
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
namespace boost::polygon {
|
namespace boost::polygon {
|
||||||
|
@ -33,8 +33,8 @@ class BoundingBox3;
|
|||||||
|
|
||||||
// Reduces polyline in the <begin, end) range, outputs into the output iterator.
|
// Reduces polyline in the <begin, end) range, outputs into the output iterator.
|
||||||
// Output iterator may be equal to input iterator as long as the iterator value type move operator supports move at the same input / output address.
|
// Output iterator may be equal to input iterator as long as the iterator value type move operator supports move at the same input / output address.
|
||||||
template<typename SquareLengthType, typename InputIterator, typename OutputIterator, typename PointGetter>
|
template<typename SquareLengthType, typename InputIterator, typename OutputIterator, typename TakeFloaterPredicate, typename PointGetter>
|
||||||
inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, const double tolerance, PointGetter point_getter)
|
inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, TakeFloaterPredicate take_floater_predicate, PointGetter point_getter)
|
||||||
{
|
{
|
||||||
using InputIteratorCategory = typename std::iterator_traits<InputIterator>::iterator_category;
|
using InputIteratorCategory = typename std::iterator_traits<InputIterator>::iterator_category;
|
||||||
static_assert(std::is_base_of_v<std::input_iterator_tag, InputIteratorCategory>);
|
static_assert(std::is_base_of_v<std::input_iterator_tag, InputIteratorCategory>);
|
||||||
@ -50,7 +50,6 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou
|
|||||||
// Two points input.
|
// Two points input.
|
||||||
*out ++ = std::move(*next);
|
*out ++ = std::move(*next);
|
||||||
} else {
|
} else {
|
||||||
const auto tolerance_sq = SquareLengthType(sqr(tolerance));
|
|
||||||
InputIterator anchor = begin;
|
InputIterator anchor = begin;
|
||||||
InputIterator floater = std::prev(end);
|
InputIterator floater = std::prev(end);
|
||||||
std::vector<InputIterator> dpStack;
|
std::vector<InputIterator> dpStack;
|
||||||
@ -66,17 +65,17 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou
|
|||||||
// Two point segment. Accept the floater.
|
// Two point segment. Accept the floater.
|
||||||
take_floater = true;
|
take_floater = true;
|
||||||
} else {
|
} else {
|
||||||
SquareLengthType max_dist_sq = 0;
|
std::optional<SquareLengthType> max_dist_sq;
|
||||||
// Find point furthest from line seg created by (anchor, floater) and note it.
|
// Find point furthest from line seg created by (anchor, floater) and note it.
|
||||||
const Vector v = (f - a).template cast<SquareLengthType>();
|
const Vector v = (f - a).template cast<SquareLengthType>();
|
||||||
if (const SquareLengthType l2 = v.squaredNorm(); l2 == 0) {
|
if (const SquareLengthType l2 = v.squaredNorm(); l2 == 0) {
|
||||||
// Zero length segment, find the furthest point between anchor and floater.
|
// Zero length segment, find the furthest point between anchor and floater.
|
||||||
for (auto it = std::next(anchor); it != floater; ++ it)
|
for (auto it = std::next(anchor); it != floater; ++ it) {
|
||||||
if (SquareLengthType dist_sq = (point_getter(*it) - a).template cast<SquareLengthType>().squaredNorm();
|
if (SquareLengthType dist_sq = (point_getter(*it) - a).template cast<SquareLengthType>().squaredNorm(); !max_dist_sq.has_value() || dist_sq > max_dist_sq) {
|
||||||
dist_sq > max_dist_sq) {
|
|
||||||
max_dist_sq = dist_sq;
|
max_dist_sq = dist_sq;
|
||||||
furthest = it;
|
furthest = it;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Find Find the furthest point from the line <anchor, floater>.
|
// Find Find the furthest point from the line <anchor, floater>.
|
||||||
const double dl2 = double(l2);
|
const double dl2 = double(l2);
|
||||||
@ -98,15 +97,20 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou
|
|||||||
const Vector w = (dt * dv).cast<SquareLengthType>();
|
const Vector w = (dt * dv).cast<SquareLengthType>();
|
||||||
dist_sq = (w - va).squaredNorm();
|
dist_sq = (w - va).squaredNorm();
|
||||||
}
|
}
|
||||||
if (dist_sq > max_dist_sq) {
|
|
||||||
|
if (!max_dist_sq.has_value() || dist_sq > max_dist_sq) {
|
||||||
max_dist_sq = dist_sq;
|
max_dist_sq = dist_sq;
|
||||||
furthest = it;
|
furthest = it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove point if less than tolerance
|
|
||||||
take_floater = max_dist_sq <= tolerance_sq;
|
assert(max_dist_sq.has_value());
|
||||||
|
|
||||||
|
// Remove points between the anchor and the floater when the predicate is satisfied.
|
||||||
|
take_floater = take_floater_predicate(anchor, floater, *max_dist_sq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (take_floater) {
|
if (take_floater) {
|
||||||
// The points between anchor and floater are close to the <anchor, floater> line.
|
// The points between anchor and floater are close to the <anchor, floater> line.
|
||||||
// Drop the points between them.
|
// Drop the points between them.
|
||||||
@ -117,6 +121,7 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou
|
|||||||
dpStack.pop_back();
|
dpStack.pop_back();
|
||||||
if (dpStack.empty())
|
if (dpStack.empty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
floater = dpStack.back();
|
floater = dpStack.back();
|
||||||
f = point_getter(*floater);
|
f = point_getter(*floater);
|
||||||
} else {
|
} else {
|
||||||
@ -132,14 +137,29 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduces polyline in the <begin, end) range, outputs into the output iterator.
|
template<typename SquareLengthType, typename InputIterator, typename OutputIterator, typename PointGetter>
|
||||||
// Output iterator may be equal to input iterator as long as the iterator value type move operator supports move at the same input / output address.
|
inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, const double tolerance, PointGetter point_getter) {
|
||||||
|
const auto tolerance_sq = static_cast<SquareLengthType>(sqr(tolerance));
|
||||||
|
|
||||||
|
const auto take_floater_predicate = [&tolerance_sq](InputIterator, InputIterator, const SquareLengthType max_dist_sq) -> bool {
|
||||||
|
return max_dist_sq <= tolerance_sq;
|
||||||
|
};
|
||||||
|
|
||||||
|
return douglas_peucker<SquareLengthType>(begin, end, out, take_floater_predicate, point_getter);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename OutputIterator>
|
template<typename OutputIterator>
|
||||||
inline OutputIterator douglas_peucker(Points::const_iterator begin, Points::const_iterator end, OutputIterator out, const double tolerance)
|
inline OutputIterator douglas_peucker(Points::const_iterator begin, Points::const_iterator end, OutputIterator out, const double tolerance)
|
||||||
{
|
{
|
||||||
return douglas_peucker<int64_t>(begin, end, out, tolerance, [](const Point &p) { return p; });
|
return douglas_peucker<int64_t>(begin, end, out, tolerance, [](const Point &p) { return p; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename OutputIterator>
|
||||||
|
inline OutputIterator douglas_peucker(Pointfs::const_iterator begin, Pointfs::const_iterator end, OutputIterator out, const double tolerance)
|
||||||
|
{
|
||||||
|
return douglas_peucker<double>(begin, end, out, tolerance, [](const Vec2d &p) { return p; });
|
||||||
|
}
|
||||||
|
|
||||||
inline Points douglas_peucker(const Points &src, const double tolerance)
|
inline Points douglas_peucker(const Points &src, const double tolerance)
|
||||||
{
|
{
|
||||||
Points out;
|
Points out;
|
||||||
@ -167,7 +187,7 @@ public:
|
|||||||
void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
|
void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
|
||||||
void rotate(double cos_angle, double sin_angle);
|
void rotate(double cos_angle, double sin_angle);
|
||||||
void rotate(double angle, const Point ¢er);
|
void rotate(double angle, const Point ¢er);
|
||||||
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
|
virtual void reverse() { std::reverse(this->points.begin(), this->points.end()); }
|
||||||
|
|
||||||
const Point& front() const { return this->points.front(); }
|
const Point& front() const { return this->points.front(); }
|
||||||
const Point& back() const { return this->points.back(); }
|
const Point& back() const { return this->points.back(); }
|
||||||
|
@ -34,11 +34,14 @@ namespace Slic3r {
|
|||||||
|
|
||||||
class Polygon;
|
class Polygon;
|
||||||
class BoundingBox;
|
class BoundingBox;
|
||||||
|
class ColorPolygon;
|
||||||
|
|
||||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||||
using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
|
using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
|
||||||
using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
|
using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
|
||||||
|
|
||||||
|
using ColorPolygons = std::vector<ColorPolygon>;
|
||||||
|
|
||||||
// Returns true if inside. Returns border_result if on boundary.
|
// Returns true if inside. Returns border_result if on boundary.
|
||||||
bool contains(const Polygon& polygon, const Point& p, bool border_result = true);
|
bool contains(const Polygon& polygon, const Point& p, bool border_result = true);
|
||||||
bool contains(const Polygons& polygons, const Point& p, bool border_result = true);
|
bool contains(const Polygons& polygons, const Point& p, bool border_result = true);
|
||||||
@ -328,12 +331,41 @@ template<class I> IntegerOnly<I, Polygons> reserve_polygons(I cap)
|
|||||||
return reserve_vector<Polygon, I, typename Polygons::allocator_type>(cap);
|
return reserve_vector<Polygon, I, typename Polygons::allocator_type>(cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Slic3r
|
class ColorPolygon : public Polygon
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Color = uint8_t;
|
||||||
|
using Colors = std::vector<Color>;
|
||||||
|
|
||||||
|
Colors colors;
|
||||||
|
|
||||||
|
ColorPolygon() = default;
|
||||||
|
explicit ColorPolygon(const Points &points, const Colors &colors) : Polygon(points), colors(colors) {}
|
||||||
|
ColorPolygon(std::initializer_list<Point> points, std::initializer_list<Color> colors) : Polygon(points), colors(colors) {}
|
||||||
|
ColorPolygon(const ColorPolygon &other) : ColorPolygon(other.points, other.colors) {}
|
||||||
|
ColorPolygon(ColorPolygon &&other) noexcept : ColorPolygon(std::move(other.points), std::move(other.colors)) {}
|
||||||
|
ColorPolygon(Points &&points, Colors &&colors) : Polygon(std::move(points)), colors(std::move(colors)) {}
|
||||||
|
|
||||||
|
void reverse() override {
|
||||||
|
Polygon::reverse();
|
||||||
|
std::reverse(this->colors.begin(), this->colors.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorPolygon &operator=(const ColorPolygon &other) {
|
||||||
|
this->points = other.points;
|
||||||
|
this->colors = other.colors;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ColorPolygons = std::vector<ColorPolygon>;
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
// start Boost
|
// start Boost
|
||||||
#include <boost/polygon/polygon.hpp>
|
#include <boost/polygon/polygon.hpp>
|
||||||
|
|
||||||
namespace boost { namespace polygon {
|
namespace boost::polygon {
|
||||||
template <>
|
template <>
|
||||||
struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
|
struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
|
||||||
|
|
||||||
@ -411,7 +443,7 @@ namespace boost { namespace polygon {
|
|||||||
polygons.assign(input_begin, input_end);
|
polygons.assign(input_begin, input_end);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} }
|
} // namespace boost::polygon
|
||||||
// end Boost
|
// end Boost
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1403,24 +1403,33 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
print_object_regions->ref_cnt_inc();
|
print_object_regions->ref_cnt_inc();
|
||||||
}
|
}
|
||||||
std::vector<unsigned int> painting_extruders;
|
std::vector<unsigned int> painting_extruders;
|
||||||
if (const auto &volumes = print_object.model_object()->volumes;
|
if (const auto &volumes = print_object.model_object()->volumes; num_extruders > 1 && print_object.model_object()->is_mm_painted()) {
|
||||||
num_extruders > 1 &&
|
|
||||||
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mm_segmentation_facets.empty(); }) != volumes.end()) {
|
|
||||||
|
|
||||||
std::array<bool, static_cast<size_t>(TriangleStateType::Count)> used_facet_states{};
|
std::array<bool, static_cast<size_t>(TriangleStateType::Count)> used_facet_states{};
|
||||||
for (const ModelVolume *volume : volumes) {
|
for (const ModelVolume *volume : volumes) {
|
||||||
|
if (volume->is_mm_painted()) {
|
||||||
const std::vector<bool> &volume_used_facet_states = volume->mm_segmentation_facets.get_data().used_states;
|
const std::vector<bool> &volume_used_facet_states = volume->mm_segmentation_facets.get_data().used_states;
|
||||||
|
|
||||||
assert(volume_used_facet_states.size() == used_facet_states.size());
|
assert(volume_used_facet_states.size() == used_facet_states.size());
|
||||||
for (size_t state_idx = 0; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx)
|
for (size_t state_idx = 1; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx) {
|
||||||
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
|
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the default facet state (TriangleStateType::NONE) is used, then we mark the volume extruder also as the used extruder.
|
||||||
|
const bool used_volume_extruder = !volume_used_facet_states.empty() && volume_used_facet_states[static_cast<size_t>(TriangleStateType::NONE)];
|
||||||
|
if (const int volume_extruder_id = volume->extruder_id(); used_volume_extruder && volume_extruder_id >= 0) {
|
||||||
|
used_facet_states[volume_extruder_id] |= true;
|
||||||
|
}
|
||||||
|
} else if (const int volume_extruder_id = volume->extruder_id(); volume_extruder_id >= 0) {
|
||||||
|
used_facet_states[volume_extruder_id] |= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t state_idx = static_cast<size_t>(TriangleStateType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
|
for (size_t state_idx = static_cast<size_t>(TriangleStateType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
|
||||||
if (used_facet_states[state_idx])
|
if (used_facet_states[state_idx]) {
|
||||||
painting_extruders.emplace_back(state_idx);
|
painting_extruders.emplace_back(state_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
|
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
|
||||||
// Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
|
// Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
|
||||||
auto invalidate = [it_print_object, it_print_object_end, update_apply_status]() {
|
auto invalidate = [it_print_object, it_print_object_end, update_apply_status]() {
|
||||||
|
@ -753,10 +753,7 @@ void PrintObject::slice_volumes()
|
|||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
|
|
||||||
// Is any ModelVolume MMU painted?
|
// Is any ModelVolume MMU painted?
|
||||||
if (const auto& volumes = this->model_object()->volumes;
|
if (m_print->config().nozzle_diameter.size() > 1 && this->model_object()->is_mm_painted()) {
|
||||||
m_print->config().nozzle_diameter.size() > 1 &&
|
|
||||||
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mm_segmentation_facets.empty(); }) != volumes.end()) {
|
|
||||||
|
|
||||||
// If XY Size compensation is also enabled, notify the user that XY Size compensation
|
// If XY Size compensation is also enabled, notify the user that XY Size compensation
|
||||||
// would not be used because the object is multi-material painted.
|
// would not be used because the object is multi-material painted.
|
||||||
if (m_config.xy_size_compensation.value != 0.f) {
|
if (m_config.xy_size_compensation.value != 0.f) {
|
||||||
|
@ -568,9 +568,10 @@ struct EdgeToFace {
|
|||||||
bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
|
bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename FaceFilter, typename ThrowOnCancelCallback>
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None, typename FaceFilter, typename ThrowOnCancelCallback>
|
||||||
static std::vector<EdgeToFace> create_edge_map(
|
static std::vector<EdgeToFace> create_edge_map(const typename IndexedTriangleSetType<mesh_info>::type &its,
|
||||||
const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
|
FaceFilter face_filter,
|
||||||
|
ThrowOnCancelCallback throw_on_cancel)
|
||||||
{
|
{
|
||||||
std::vector<EdgeToFace> edges_map;
|
std::vector<EdgeToFace> edges_map;
|
||||||
edges_map.reserve(its.indices.size() * 3);
|
edges_map.reserve(its.indices.size() * 3);
|
||||||
@ -599,12 +600,14 @@ static std::vector<EdgeToFace> create_edge_map(
|
|||||||
|
|
||||||
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
|
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
|
||||||
// Two neighbor faces share a unique edge identifier even if they are flipped.
|
// Two neighbor faces share a unique edge identifier even if they are flipped.
|
||||||
template<typename FaceFilter, typename ThrowOnCancelCallback>
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None, typename FaceFilter, typename ThrowOnCancelCallback>
|
||||||
static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
|
static inline std::vector<Vec3i> its_face_edge_ids_impl(const typename IndexedTriangleSetType<mesh_info>::type &its,
|
||||||
|
FaceFilter face_filter,
|
||||||
|
ThrowOnCancelCallback throw_on_cancel)
|
||||||
{
|
{
|
||||||
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
|
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
|
||||||
|
|
||||||
std::vector<EdgeToFace> edges_map = create_edge_map(its, face_filter, throw_on_cancel);
|
std::vector<EdgeToFace> edges_map = create_edge_map<mesh_info>(its, face_filter, throw_on_cancel);
|
||||||
|
|
||||||
// Assign a unique common edge id to touching triangle edges.
|
// Assign a unique common edge id to touching triangle edges.
|
||||||
int num_edges = 0;
|
int num_edges = 0;
|
||||||
@ -650,9 +653,16 @@ static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_s
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its)
|
// Explicit template instantiation.
|
||||||
|
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::None>(const IndexedTriangleSetType<AdditionalMeshInfo::None>::type &);
|
||||||
|
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::Color>(const IndexedTriangleSetType<AdditionalMeshInfo::Color>::type &);
|
||||||
|
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::None>(const IndexedTriangleSetType<AdditionalMeshInfo::None>::type &, const std::vector<char> &);
|
||||||
|
template std::vector<Vec3i> its_face_edge_ids<AdditionalMeshInfo::Color>(const IndexedTriangleSetType<AdditionalMeshInfo::Color>::type &, const std::vector<char> &);
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its)
|
||||||
{
|
{
|
||||||
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, [](){});
|
return its_face_edge_ids_impl<mesh_info>(its, [](const uint32_t){ return true; }, [](){});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
|
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
|
||||||
@ -660,9 +670,10 @@ std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::funct
|
|||||||
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
|
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask)
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its, const std::vector<char> &face_mask)
|
||||||
{
|
{
|
||||||
return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){});
|
return its_face_edge_ids_impl<mesh_info>(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
||||||
|
@ -39,6 +39,30 @@ namespace Slic3r {
|
|||||||
class TriangleMesh;
|
class TriangleMesh;
|
||||||
class TriangleMeshSlicer;
|
class TriangleMeshSlicer;
|
||||||
|
|
||||||
|
struct indexed_triangle_set_with_color
|
||||||
|
{
|
||||||
|
std::vector<stl_triangle_vertex_indices> indices;
|
||||||
|
std::vector<stl_vertex> vertices;
|
||||||
|
std::vector<uint8_t> colors;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AdditionalMeshInfo {
|
||||||
|
None,
|
||||||
|
Color
|
||||||
|
};
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo mesh_info> struct IndexedTriangleSetType;
|
||||||
|
|
||||||
|
template<> struct IndexedTriangleSetType<AdditionalMeshInfo::None>
|
||||||
|
{
|
||||||
|
using type = indexed_triangle_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct IndexedTriangleSetType<AdditionalMeshInfo::Color>
|
||||||
|
{
|
||||||
|
using type = indexed_triangle_set_with_color;
|
||||||
|
};
|
||||||
|
|
||||||
struct RepairedMeshErrors {
|
struct RepairedMeshErrors {
|
||||||
// How many edges were united by merging their end points with some other end points in epsilon neighborhood?
|
// How many edges were united by merging their end points with some other end points in epsilon neighborhood?
|
||||||
int edges_fixed = 0;
|
int edges_fixed = 0;
|
||||||
@ -210,9 +234,14 @@ private:
|
|||||||
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
|
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
|
||||||
// Two neighbor faces share a unique edge identifier even if they are flipped.
|
// Two neighbor faces share a unique edge identifier even if they are flipped.
|
||||||
// Used for chaining slice lines into polygons.
|
// Used for chaining slice lines into polygons.
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
|
||||||
|
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its);
|
||||||
|
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
|
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask);
|
|
||||||
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
|
||||||
|
std::vector<Vec3i> its_face_edge_ids(const typename IndexedTriangleSetType<mesh_info>::type &its, const std::vector<char> &face_mask);
|
||||||
|
|
||||||
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
||||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);
|
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);
|
||||||
|
|
||||||
@ -394,7 +423,7 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its, const Transfo
|
|||||||
return {bmin.cast<double>(), bmax.cast<double>()};
|
return {bmin.cast<double>(), bmax.cast<double>()};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Slic3r
|
||||||
|
|
||||||
// Serialization through the Cereal library
|
// Serialization through the Cereal library
|
||||||
#include <cereal/access.hpp>
|
#include <cereal/access.hpp>
|
||||||
|
@ -58,6 +58,39 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo mesh_info> struct PolygonsType;
|
||||||
|
|
||||||
|
template<> struct PolygonsType<AdditionalMeshInfo::None>
|
||||||
|
{
|
||||||
|
using type = Polygons;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct PolygonsType<AdditionalMeshInfo::Color>
|
||||||
|
{
|
||||||
|
using type = ColorPolygons;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo mesh_info> struct FacetColorFunctor;
|
||||||
|
|
||||||
|
template<> struct FacetColorFunctor<AdditionalMeshInfo::None>
|
||||||
|
{
|
||||||
|
constexpr ColorPolygon::Color operator()(size_t facet_idx) const { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct FacetColorFunctor<AdditionalMeshInfo::Color>
|
||||||
|
{
|
||||||
|
FacetColorFunctor() = delete;
|
||||||
|
explicit FacetColorFunctor(const ColorPolygon::Colors &colors) : colors(colors) {}
|
||||||
|
|
||||||
|
ColorPolygon::Color operator()(size_t facet_idx) const {
|
||||||
|
assert(facet_idx < this->colors.size());
|
||||||
|
return this->colors[facet_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ColorPolygon::Colors &colors;
|
||||||
|
};
|
||||||
|
|
||||||
class IntersectionReference
|
class IntersectionReference
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -141,7 +174,10 @@ public:
|
|||||||
NO_SEED = 0x100,
|
NO_SEED = 0x100,
|
||||||
SKIP = 0x200,
|
SKIP = 0x200,
|
||||||
};
|
};
|
||||||
uint32_t flags { 0 };
|
|
||||||
|
uint16_t flags { 0 };
|
||||||
|
// Color id of sliced facet.
|
||||||
|
uint8_t color { 0 };
|
||||||
|
|
||||||
#ifdef DEBUG_INTERSECTIONLINE
|
#ifdef DEBUG_INTERSECTIONLINE
|
||||||
enum class Source {
|
enum class Source {
|
||||||
@ -193,6 +229,7 @@ inline FacetSliceType slice_facet(
|
|||||||
const Vec3i &edge_ids,
|
const Vec3i &edge_ids,
|
||||||
const int idx_vertex_lowest,
|
const int idx_vertex_lowest,
|
||||||
const bool horizontal,
|
const bool horizontal,
|
||||||
|
const ColorPolygon::Color facet_color,
|
||||||
IntersectionLine &line_out)
|
IntersectionLine &line_out)
|
||||||
{
|
{
|
||||||
using Vector = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
|
using Vector = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
|
||||||
@ -256,6 +293,7 @@ inline FacetSliceType slice_facet(
|
|||||||
line_out.b = v3f_scaled_to_contour_point(*b);
|
line_out.b = v3f_scaled_to_contour_point(*b);
|
||||||
line_out.a_id = a_id;
|
line_out.a_id = a_id;
|
||||||
line_out.b_id = b_id;
|
line_out.b_id = b_id;
|
||||||
|
line_out.color = facet_color;
|
||||||
assert(line_out.a != line_out.b);
|
assert(line_out.a != line_out.b);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -333,6 +371,7 @@ inline FacetSliceType slice_facet(
|
|||||||
line_out.b_id = points[0].point_id;
|
line_out.b_id = points[0].point_id;
|
||||||
line_out.edge_a_id = points[1].edge_id;
|
line_out.edge_a_id = points[1].edge_id;
|
||||||
line_out.edge_b_id = points[0].edge_id;
|
line_out.edge_b_id = points[0].edge_id;
|
||||||
|
line_out.color = facet_color;
|
||||||
// Not a zero lenght edge.
|
// Not a zero lenght edge.
|
||||||
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
||||||
//assert(line_out.a != line_out.b);
|
//assert(line_out.a != line_out.b);
|
||||||
@ -387,6 +426,7 @@ void slice_facet_at_zs(
|
|||||||
const TransformVertex &transform_vertex_fn,
|
const TransformVertex &transform_vertex_fn,
|
||||||
const stl_triangle_vertex_indices &indices,
|
const stl_triangle_vertex_indices &indices,
|
||||||
const Vec3i &edge_ids,
|
const Vec3i &edge_ids,
|
||||||
|
const ColorPolygon::Color facet_color,
|
||||||
// Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well.
|
// Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well.
|
||||||
const std::vector<float> &zs,
|
const std::vector<float> &zs,
|
||||||
std::vector<IntersectionLines> &lines,
|
std::vector<IntersectionLines> &lines,
|
||||||
@ -406,7 +446,7 @@ void slice_facet_at_zs(
|
|||||||
for (auto it = min_layer; it != max_layer; ++ it) {
|
for (auto it = min_layer; it != max_layer; ++ it) {
|
||||||
IntersectionLine il;
|
IntersectionLine il;
|
||||||
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
|
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
|
||||||
if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
|
if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, facet_color, il) == FacetSliceType::Slicing) {
|
||||||
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
||||||
size_t slice_id = it - zs.begin();
|
size_t slice_id = it - zs.begin();
|
||||||
boost::lock_guard<std::mutex> l(lines_mutex(slice_id));
|
boost::lock_guard<std::mutex> l(lines_mutex(slice_id));
|
||||||
@ -415,12 +455,13 @@ void slice_facet_at_zs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TransformVertex, typename ThrowOnCancel>
|
template<AdditionalMeshInfo mesh_info, typename TransformVertex, typename ThrowOnCancel>
|
||||||
static inline std::vector<IntersectionLines> slice_make_lines(
|
static inline std::vector<IntersectionLines> slice_make_lines(
|
||||||
const std::vector<stl_vertex> &vertices,
|
const std::vector<stl_vertex> &vertices,
|
||||||
const TransformVertex &transform_vertex_fn,
|
const TransformVertex &transform_vertex_fn,
|
||||||
const std::vector<stl_triangle_vertex_indices> &indices,
|
const std::vector<stl_triangle_vertex_indices> &indices,
|
||||||
const std::vector<Vec3i> &face_edge_ids,
|
const std::vector<Vec3i> &face_edge_ids,
|
||||||
|
const FacetColorFunctor<mesh_info> &facet_color_fn,
|
||||||
const std::vector<float> &zs,
|
const std::vector<float> &zs,
|
||||||
const ThrowOnCancel throw_on_cancel_fn)
|
const ThrowOnCancel throw_on_cancel_fn)
|
||||||
{
|
{
|
||||||
@ -428,28 +469,30 @@ static inline std::vector<IntersectionLines> slice_make_lines(
|
|||||||
LinesMutexes lines_mutex;
|
LinesMutexes lines_mutex;
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<int>(0, int(indices.size())),
|
tbb::blocked_range<int>(0, int(indices.size())),
|
||||||
[&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
|
[&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &facet_color_fn, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
|
||||||
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
|
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
|
||||||
if ((face_idx & 0x0ffff) == 0)
|
if ((face_idx & 0x0ffff) == 0)
|
||||||
throw_on_cancel_fn();
|
throw_on_cancel_fn();
|
||||||
slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], zs, lines, lines_mutex);
|
slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], facet_color_fn(face_idx), zs, lines, lines_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TransformVertex, typename FaceFilter>
|
template<AdditionalMeshInfo mesh_info, typename TransformVertex, typename FaceFilter>
|
||||||
static inline IntersectionLines slice_make_lines(
|
static inline IntersectionLines slice_make_lines(
|
||||||
const std::vector<stl_vertex> &mesh_vertices,
|
const std::vector<stl_vertex> &mesh_vertices,
|
||||||
const TransformVertex &transform_vertex_fn,
|
const TransformVertex &transform_vertex_fn,
|
||||||
const std::vector<stl_triangle_vertex_indices> &mesh_faces,
|
const std::vector<stl_triangle_vertex_indices> &mesh_faces,
|
||||||
const std::vector<Vec3i> &face_edge_ids,
|
const std::vector<Vec3i> &face_edge_ids,
|
||||||
|
const FacetColorFunctor<mesh_info> &facet_color_fn,
|
||||||
const float plane_z,
|
const float plane_z,
|
||||||
FaceFilter face_filter)
|
FaceFilter face_filter)
|
||||||
{
|
{
|
||||||
IntersectionLines lines;
|
IntersectionLines lines;
|
||||||
for (int face_idx = 0; face_idx < int(mesh_faces.size()); ++ face_idx)
|
for (int face_idx = 0; face_idx < int(mesh_faces.size()); ++ face_idx) {
|
||||||
if (face_filter(face_idx)) {
|
if (face_filter(face_idx)) {
|
||||||
const Vec3i &indices = mesh_faces[face_idx];
|
const Vec3i &indices = mesh_faces[face_idx];
|
||||||
stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
|
stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
|
||||||
@ -460,11 +503,13 @@ static inline IntersectionLines slice_make_lines(
|
|||||||
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
|
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
|
||||||
IntersectionLine il;
|
IntersectionLine il;
|
||||||
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
|
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
|
||||||
if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
|
if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, facet_color_fn(face_idx), il) == FacetSliceType::Slicing) {
|
||||||
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
||||||
lines.emplace_back(il);
|
lines.emplace_back(il);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,7 +651,7 @@ void slice_facet_with_slabs(
|
|||||||
IntersectionLine il_prev;
|
IntersectionLine il_prev;
|
||||||
for (auto it = min_layer; it != max_layer; ++ it) {
|
for (auto it = min_layer; it != max_layer; ++ it) {
|
||||||
IntersectionLine il;
|
IntersectionLine il;
|
||||||
auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, il);
|
auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, 0, il);
|
||||||
if (type == FacetSliceType::NoSlice) {
|
if (type == FacetSliceType::NoSlice) {
|
||||||
// One and exactly one vertex is touching the slicing plane.
|
// One and exactly one vertex is touching the slicing plane.
|
||||||
} else {
|
} else {
|
||||||
@ -950,8 +995,8 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
|
|||||||
|
|
||||||
struct OpenPolyline {
|
struct OpenPolyline {
|
||||||
OpenPolyline() = default;
|
OpenPolyline() = default;
|
||||||
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
|
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points, ColorPolygon::Colors &&colors) :
|
||||||
start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); }
|
start(start), end(end), points(std::move(points)), colors(std::move(colors)), length(Slic3r::length(this->points)), consumed(false) {}
|
||||||
void reverse() {
|
void reverse() {
|
||||||
std::swap(start, end);
|
std::swap(start, end);
|
||||||
std::reverse(points.begin(), points.end());
|
std::reverse(points.begin(), points.end());
|
||||||
@ -959,13 +1004,17 @@ struct OpenPolyline {
|
|||||||
IntersectionReference start;
|
IntersectionReference start;
|
||||||
IntersectionReference end;
|
IntersectionReference end;
|
||||||
Points points;
|
Points points;
|
||||||
|
ColorPolygon::Colors colors;
|
||||||
double length;
|
double length;
|
||||||
bool consumed;
|
bool consumed;
|
||||||
};
|
};
|
||||||
|
|
||||||
// called by make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
|
// called by make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
|
||||||
// Only connects segments crossing triangles of the same orientation.
|
// Only connects segments crossing triangles of the same orientation.
|
||||||
static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
static void chain_lines_by_triangle_connectivity(IntersectionLines &lines,
|
||||||
|
typename PolygonsType<mesh_info>::type &loops,
|
||||||
|
std::vector<OpenPolyline> &open_polylines)
|
||||||
{
|
{
|
||||||
// Build a map of lines by edge_a_id and a_id.
|
// Build a map of lines by edge_a_id and a_id.
|
||||||
std::vector<IntersectionLine*> by_edge_a_id;
|
std::vector<IntersectionLine*> by_edge_a_id;
|
||||||
@ -997,9 +1046,16 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
|
|||||||
}
|
}
|
||||||
if (first_line == nullptr)
|
if (first_line == nullptr)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
first_line->set_skip();
|
first_line->set_skip();
|
||||||
Points loop_pts;
|
Points loop_pts;
|
||||||
loop_pts.emplace_back(first_line->a);
|
loop_pts.emplace_back(first_line->a);
|
||||||
|
|
||||||
|
ColorPolygon::Colors loop_colors;
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color) {
|
||||||
|
loop_colors.emplace_back(first_line->color);
|
||||||
|
}
|
||||||
|
|
||||||
IntersectionLine *last_line = first_line;
|
IntersectionLine *last_line = first_line;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1042,7 +1098,13 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
|
|||||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||||
// The current loop is complete. Add it to the output.
|
// The current loop is complete. Add it to the output.
|
||||||
assert(first_line->a == last_line->b);
|
assert(first_line->a == last_line->b);
|
||||||
|
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color) {
|
||||||
|
loops.emplace_back(std::move(loop_pts), std::move(loop_colors));
|
||||||
|
} else {
|
||||||
loops.emplace_back(std::move(loop_pts));
|
loops.emplace_back(std::move(loop_pts));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||||
#endif
|
#endif
|
||||||
@ -1051,7 +1113,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
|
|||||||
loop_pts.emplace_back(last_line->b);
|
loop_pts.emplace_back(last_line->b);
|
||||||
open_polylines.emplace_back(OpenPolyline(
|
open_polylines.emplace_back(OpenPolyline(
|
||||||
IntersectionReference(first_line->a_id, first_line->edge_a_id),
|
IntersectionReference(first_line->a_id, first_line->edge_a_id),
|
||||||
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
|
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts), std::move(loop_colors)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1062,6 +1124,11 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
|
|||||||
*/
|
*/
|
||||||
assert(last_line->b == next_line->a);
|
assert(last_line->b == next_line->a);
|
||||||
loop_pts.emplace_back(next_line->a);
|
loop_pts.emplace_back(next_line->a);
|
||||||
|
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color) {
|
||||||
|
loop_colors.emplace_back(next_line->color);
|
||||||
|
}
|
||||||
|
|
||||||
last_line = next_line;
|
last_line = next_line;
|
||||||
next_line->set_skip();
|
next_line->set_skip();
|
||||||
}
|
}
|
||||||
@ -1084,7 +1151,10 @@ std::vector<OpenPolyline*> open_polylines_sorted(std::vector<OpenPolyline> &open
|
|||||||
|
|
||||||
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices.
|
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices.
|
||||||
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
||||||
static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines, Polygons &loops, bool try_connect_reversed)
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines,
|
||||||
|
typename PolygonsType<mesh_info>::type &loops,
|
||||||
|
bool try_connect_reversed)
|
||||||
{
|
{
|
||||||
// Store the end points of open_polylines into vectors sorted
|
// Store the end points of open_polylines into vectors sorted
|
||||||
struct OpenPolylineEnd {
|
struct OpenPolylineEnd {
|
||||||
@ -1109,7 +1179,7 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
|
|||||||
}
|
}
|
||||||
std::sort(by_id.begin(), by_id.end(), by_id_lower);
|
std::sort(by_id.begin(), by_id.end(), by_id_lower);
|
||||||
// Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute).
|
// Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute).
|
||||||
auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> std::vector<OpenPolylineEnd>::iterator {
|
auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> typename std::vector<OpenPolylineEnd>::iterator {
|
||||||
for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
|
for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
|
||||||
it != by_id.end() && it->id() == end.id(); ++ it)
|
it != by_id.end() && it->id() == end.id(); ++ it)
|
||||||
if (*it == end)
|
if (*it == end)
|
||||||
@ -1135,15 +1205,20 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
|
|||||||
found:
|
found:
|
||||||
// Attach this polyline to the end of the initial polyline.
|
// Attach this polyline to the end of the initial polyline.
|
||||||
if (it_next_start->start) {
|
if (it_next_start->start) {
|
||||||
auto it = it_next_start->polyline->points.begin();
|
auto pt_it = it_next_start->polyline->points.begin();
|
||||||
std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points));
|
auto color_it = it_next_start->polyline->colors.begin();
|
||||||
|
std::copy(++pt_it, it_next_start->polyline->points.end(), back_inserter(opl->points));
|
||||||
|
std::copy(color_it, it_next_start->polyline->colors.end(), back_inserter(opl->colors));
|
||||||
} else {
|
} else {
|
||||||
auto it = it_next_start->polyline->points.rbegin();
|
auto pt_it = it_next_start->polyline->points.rbegin();
|
||||||
std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points));
|
auto color_it = it_next_start->polyline->colors.rbegin();
|
||||||
|
std::copy(++pt_it, it_next_start->polyline->points.rend(), back_inserter(opl->points));
|
||||||
|
std::copy(color_it, it_next_start->polyline->colors.rend(), back_inserter(opl->colors));
|
||||||
}
|
}
|
||||||
opl->length += it_next_start->polyline->length;
|
opl->length += it_next_start->polyline->length;
|
||||||
// Mark the next polyline as consumed.
|
// Mark the next polyline as consumed.
|
||||||
it_next_start->polyline->points.clear();
|
it_next_start->polyline->points.clear();
|
||||||
|
it_next_start->polyline->colors.clear();
|
||||||
it_next_start->polyline->length = 0.;
|
it_next_start->polyline->length = 0.;
|
||||||
it_next_start->polyline->consumed = true;
|
it_next_start->polyline->consumed = true;
|
||||||
if (try_connect_reversed) {
|
if (try_connect_reversed) {
|
||||||
@ -1163,16 +1238,26 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
|
|||||||
//assert(opl->points.front().point_id == opl->points.back().point_id);
|
//assert(opl->points.front().point_id == opl->points.back().point_id);
|
||||||
//assert(opl->points.front().edge_id == opl->points.back().edge_id);
|
//assert(opl->points.front().edge_id == opl->points.back().edge_id);
|
||||||
// Remove the duplicate last point.
|
// Remove the duplicate last point.
|
||||||
|
// Contrary to the points, the assigned colors will not be duplicated, so we will not remove them.
|
||||||
opl->points.pop_back();
|
opl->points.pop_back();
|
||||||
if (opl->points.size() >= 3) {
|
if (opl->points.size() >= 3) {
|
||||||
if (try_connect_reversed && area(opl->points) < 0)
|
if (try_connect_reversed && area(opl->points) < 0) {
|
||||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||||
// the orientation of the patched up polygon is not known.
|
// the orientation of the patched up polygon is not known.
|
||||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||||
std::reverse(opl->points.begin(), opl->points.end());
|
std::reverse(opl->points.begin(), opl->points.end());
|
||||||
|
std::reverse(opl->colors.begin(), opl->colors.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color) {
|
||||||
|
loops.emplace_back(std::move(opl->points), std::move(opl->colors));
|
||||||
|
} else {
|
||||||
loops.emplace_back(std::move(opl->points));
|
loops.emplace_back(std::move(opl->points));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opl->points.clear();
|
opl->points.clear();
|
||||||
|
opl->colors.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Continue with the current loop.
|
// Continue with the current loop.
|
||||||
@ -1180,10 +1265,41 @@ static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The midpoint is inserted when color differs on both endpoints.
|
||||||
|
// Return true when a midpoint is inserted.
|
||||||
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
bool handle_color_at_gap_between_open_polylines(OpenPolyline &opl,
|
||||||
|
const Point &next_polyline_first_pt,
|
||||||
|
const ColorPolygon::Color &next_polyline_first_color)
|
||||||
|
{
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color) {
|
||||||
|
bool midpoint_inserted = false;
|
||||||
|
if (opl.colors.back() == next_polyline_first_color) {
|
||||||
|
// Both endpoints around the gap have the same color, so we also use the same color for the gap.
|
||||||
|
opl.colors.emplace_back(opl.colors.back());
|
||||||
|
} else {
|
||||||
|
// Endpoints around the gap have different colors, so we split the gap into two pieces,
|
||||||
|
// each with a different color.
|
||||||
|
opl.points.emplace_back(line_alg::midpoint(opl.points.back(), next_polyline_first_pt));
|
||||||
|
opl.colors.emplace_back(opl.colors.back());
|
||||||
|
opl.colors.emplace_back(next_polyline_first_color);
|
||||||
|
midpoint_inserted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return midpoint_inserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices,
|
// called by make_loops() to connect remaining open polylines across shared triangle edges and vertices,
|
||||||
// possibly closing small gaps.
|
// possibly closing small gaps.
|
||||||
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
||||||
static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed)
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines,
|
||||||
|
typename PolygonsType<mesh_info>::type &loops,
|
||||||
|
double max_gap,
|
||||||
|
bool try_connect_reversed)
|
||||||
{
|
{
|
||||||
const coord_t max_gap_scaled = (coord_t)scale_(max_gap);
|
const coord_t max_gap_scaled = (coord_t)scale_(max_gap);
|
||||||
|
|
||||||
@ -1214,10 +1330,13 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
|
|||||||
for (OpenPolyline *opl : sorted_by_length) {
|
for (OpenPolyline *opl : sorted_by_length) {
|
||||||
if (opl->consumed)
|
if (opl->consumed)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
OpenPolylineEnd end(opl, false);
|
OpenPolylineEnd end(opl, false);
|
||||||
if (try_connect_reversed)
|
if (try_connect_reversed) {
|
||||||
// The end point of this polyline will be modified, thus the following entry will become invalid. Remove it.
|
// The end point of this polyline will be modified, thus the following entry will become invalid. Remove it.
|
||||||
closest_end_point_lookup.erase(end);
|
closest_end_point_lookup.erase(end);
|
||||||
|
}
|
||||||
|
|
||||||
opl->consumed = true;
|
opl->consumed = true;
|
||||||
size_t n_segments_joined = 1;
|
size_t n_segments_joined = 1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -1226,7 +1345,7 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
|
|||||||
const OpenPolylineEnd *next_start = next_start_and_dist.first;
|
const OpenPolylineEnd *next_start = next_start_and_dist.first;
|
||||||
// Check whether we closed this loop.
|
// Check whether we closed this loop.
|
||||||
double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast<double>().squaredNorm();
|
double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast<double>().squaredNorm();
|
||||||
bool loop_closed = current_loop_closing_distance2 < coordf_t(max_gap_scaled) * coordf_t(max_gap_scaled);
|
bool loop_closed = current_loop_closing_distance2 < Slic3r::sqr(coordf_t(max_gap_scaled));
|
||||||
if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) {
|
if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) {
|
||||||
// Heuristics to decide, whether to close the loop, or connect another polyline.
|
// Heuristics to decide, whether to close the loop, or connect another polyline.
|
||||||
// One should avoid closing loops shorter than max_gap_scaled.
|
// One should avoid closing loops shorter than max_gap_scaled.
|
||||||
@ -1237,21 +1356,35 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
|
|||||||
// Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail.
|
// Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail.
|
||||||
opl->consumed = false;
|
opl->consumed = false;
|
||||||
closest_end_point_lookup.erase(OpenPolylineEnd(opl, true));
|
closest_end_point_lookup.erase(OpenPolylineEnd(opl, true));
|
||||||
|
|
||||||
|
bool midpoint_inserted = false;
|
||||||
if (current_loop_closing_distance2 == 0.) {
|
if (current_loop_closing_distance2 == 0.) {
|
||||||
// Remove the duplicate last point.
|
// Remove the duplicate last point.
|
||||||
opl->points.pop_back();
|
opl->points.pop_back();
|
||||||
} else {
|
} else {
|
||||||
// The end points are different, keep both of them.
|
// The end points are different, keep both of them.
|
||||||
|
midpoint_inserted = handle_color_at_gap_between_open_polylines<mesh_info>(*opl, opl->points.front(), opl->colors.front());
|
||||||
}
|
}
|
||||||
if (opl->points.size() >= 3) {
|
|
||||||
if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0)
|
// When we split the gap into two pieces by adding a midpoint, then a valid polygon has at least 4 points.
|
||||||
|
if (opl->points.size() >= (3 + size_t(midpoint_inserted))) {
|
||||||
|
if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0) {
|
||||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||||
// the orientation of the patched up polygon is not known.
|
// the orientation of the patched up polygon is not known.
|
||||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||||
std::reverse(opl->points.begin(), opl->points.end());
|
std::reverse(opl->points.begin(), opl->points.end());
|
||||||
|
std::reverse(opl->colors.begin(), opl->colors.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color) {
|
||||||
|
loops.emplace_back(std::move(opl->points), std::move(opl->colors));
|
||||||
|
} else {
|
||||||
loops.emplace_back(std::move(opl->points));
|
loops.emplace_back(std::move(opl->points));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opl->points.clear();
|
opl->points.clear();
|
||||||
|
opl->colors.clear();
|
||||||
opl->consumed = true;
|
opl->consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1263,36 +1396,56 @@ static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_poly
|
|||||||
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
|
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach this polyline to the end of the initial polyline.
|
// Attach this polyline to the end of the initial polyline.
|
||||||
if (next_start->start) {
|
if (next_start->start) {
|
||||||
auto it = next_start->polyline->points.begin();
|
auto pt_it = next_start->polyline->points.begin();
|
||||||
if (*it == opl->points.back())
|
auto color_it = next_start->polyline->colors.begin();
|
||||||
++ it;
|
if (*pt_it == opl->points.back()) {
|
||||||
std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points));
|
++pt_it;
|
||||||
} else {
|
} else {
|
||||||
auto it = next_start->polyline->points.rbegin();
|
handle_color_at_gap_between_open_polylines<mesh_info>(*opl, *pt_it, *color_it);
|
||||||
if (*it == opl->points.back())
|
|
||||||
++ it;
|
|
||||||
std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::copy(pt_it, next_start->polyline->points.end(), back_inserter(opl->points));
|
||||||
|
std::copy(color_it, next_start->polyline->colors.end(), back_inserter(opl->colors));
|
||||||
|
} else {
|
||||||
|
auto pt_it = next_start->polyline->points.rbegin();
|
||||||
|
auto color_it = next_start->polyline->colors.rbegin();
|
||||||
|
if (*pt_it == opl->points.back()) {
|
||||||
|
++pt_it;
|
||||||
|
} else {
|
||||||
|
handle_color_at_gap_between_open_polylines<mesh_info>(*opl, *pt_it, *color_it);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy(pt_it, next_start->polyline->points.rend(), back_inserter(opl->points));
|
||||||
|
std::copy(color_it, next_start->polyline->colors.rend(), back_inserter(opl->colors));
|
||||||
|
}
|
||||||
|
|
||||||
++n_segments_joined;
|
++n_segments_joined;
|
||||||
// Remove the end points of the consumed polyline segment from the lookup.
|
// Remove the end points of the consumed polyline segment from the lookup.
|
||||||
OpenPolyline *opl2 = next_start->polyline;
|
OpenPolyline *opl2 = next_start->polyline;
|
||||||
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true));
|
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true));
|
||||||
if (try_connect_reversed)
|
if (try_connect_reversed) {
|
||||||
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false));
|
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false));
|
||||||
|
}
|
||||||
|
|
||||||
opl2->points.clear();
|
opl2->points.clear();
|
||||||
|
opl2->colors.clear();
|
||||||
opl2->consumed = true;
|
opl2->consumed = true;
|
||||||
// Continue with the current loop.
|
// Continue with the current loop.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Polygons make_loops(
|
template<AdditionalMeshInfo mesh_info>
|
||||||
|
static typename PolygonsType<mesh_info>::type make_loops(
|
||||||
// Lines will have their flags modified.
|
// Lines will have their flags modified.
|
||||||
IntersectionLines &lines)
|
IntersectionLines &lines
|
||||||
{
|
) {
|
||||||
Polygons loops;
|
using PolygonsType = typename PolygonsType<mesh_info>::type;
|
||||||
|
|
||||||
|
PolygonsType loops;
|
||||||
#if 0
|
#if 0
|
||||||
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
||||||
//#ifdef _DEBUG
|
//#ifdef _DEBUG
|
||||||
@ -1320,7 +1473,7 @@ static Polygons make_loops(
|
|||||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
std::vector<OpenPolyline> open_polylines;
|
std::vector<OpenPolyline> open_polylines;
|
||||||
chain_lines_by_triangle_connectivity(lines, loops, open_polylines);
|
chain_lines_by_triangle_connectivity<mesh_info>(lines, loops, open_polylines);
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
{
|
{
|
||||||
@ -1336,8 +1489,8 @@ static Polygons make_loops(
|
|||||||
// Now process the open polylines.
|
// Now process the open polylines.
|
||||||
// Do it in two rounds, first try to connect in the same direction only,
|
// Do it in two rounds, first try to connect in the same direction only,
|
||||||
// then try to connect the open polylines in reversed order as well.
|
// then try to connect the open polylines in reversed order as well.
|
||||||
chain_open_polylines_exact(open_polylines, loops, false);
|
chain_open_polylines_exact<mesh_info>(open_polylines, loops, false);
|
||||||
chain_open_polylines_exact(open_polylines, loops, true);
|
chain_open_polylines_exact<mesh_info>(open_polylines, loops, true);
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
{
|
{
|
||||||
@ -1365,8 +1518,8 @@ static Polygons make_loops(
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
const double max_gap = 2.; //mm
|
const double max_gap = 2.; //mm
|
||||||
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, false);
|
chain_open_polylines_close_gaps<mesh_info>(open_polylines, loops, max_gap, false);
|
||||||
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, true);
|
chain_open_polylines_close_gaps<mesh_info>(open_polylines, loops, max_gap, true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
@ -1388,14 +1541,17 @@ static Polygons make_loops(
|
|||||||
return loops;
|
return loops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ThrowOnCancel>
|
template<AdditionalMeshInfo mesh_info, typename ThrowOnCancel>
|
||||||
static std::vector<Polygons> make_loops(
|
static std::vector<typename PolygonsType<mesh_info>::type> make_loops(
|
||||||
// Lines will have their flags modified.
|
// Lines will have their flags modified.
|
||||||
std::vector<IntersectionLines> &lines,
|
std::vector<IntersectionLines> &lines,
|
||||||
const MeshSlicingParams ¶ms,
|
const MeshSlicingParams ¶ms,
|
||||||
ThrowOnCancel throw_on_cancel)
|
ThrowOnCancel throw_on_cancel)
|
||||||
{
|
{
|
||||||
std::vector<Polygons> layers;
|
using PolygonsType = typename PolygonsType<mesh_info>::type;
|
||||||
|
using PolygonType = typename PolygonsType::value_type;
|
||||||
|
|
||||||
|
std::vector<PolygonsType> layers;
|
||||||
layers.resize(lines.size());
|
layers.resize(lines.size());
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, lines.size()),
|
tbb::blocked_range<size_t>(0, lines.size()),
|
||||||
@ -1404,31 +1560,33 @@ static std::vector<Polygons> make_loops(
|
|||||||
if ((line_idx & 0x0ffff) == 0)
|
if ((line_idx & 0x0ffff) == 0)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
|
|
||||||
Polygons &polygons = layers[line_idx];
|
PolygonsType &polygons = layers[line_idx];
|
||||||
polygons = make_loops(lines[line_idx]);
|
polygons = make_loops<mesh_info>(lines[line_idx]);
|
||||||
|
|
||||||
auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
|
auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
|
||||||
if (! polygons.empty()) {
|
if (! polygons.empty()) {
|
||||||
if (this_mode == MeshSlicingParams::SlicingMode::Positive) {
|
if (this_mode == MeshSlicingParams::SlicingMode::Positive) {
|
||||||
// Reorient all loops to be CCW.
|
// Reorient all loops to be CCW.
|
||||||
for (Polygon& p : polygons)
|
for (PolygonType &p : polygons) {
|
||||||
p.make_counter_clockwise();
|
p.make_counter_clockwise();
|
||||||
}
|
}
|
||||||
else if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) {
|
} else if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) {
|
||||||
// Keep just the largest polygon, make it CCW.
|
// Keep just the largest polygon, make it CCW.
|
||||||
double max_area = 0.;
|
double max_area = 0.;
|
||||||
Polygon* max_area_polygon = nullptr;
|
PolygonType *max_area_polygon = nullptr;
|
||||||
for (Polygon& p : polygons) {
|
for (PolygonType &p : polygons) {
|
||||||
double a = p.area();
|
if (const double a = p.area(); std::abs(a) > std::abs(max_area)) {
|
||||||
if (std::abs(a) > std::abs(max_area)) {
|
|
||||||
max_area = a;
|
max_area = a;
|
||||||
max_area_polygon = &p;
|
max_area_polygon = &p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(max_area_polygon != nullptr);
|
assert(max_area_polygon != nullptr);
|
||||||
if (max_area < 0.)
|
if (max_area < 0.) {
|
||||||
max_area_polygon->reverse();
|
max_area_polygon->reverse();
|
||||||
Polygon p(std::move(*max_area_polygon));
|
}
|
||||||
|
|
||||||
|
PolygonType p(std::move(*max_area_polygon));
|
||||||
polygons.clear();
|
polygons.clear();
|
||||||
polygons.emplace_back(std::move(p));
|
polygons.emplace_back(std::move(p));
|
||||||
}
|
}
|
||||||
@ -1534,7 +1692,7 @@ static std::vector<Polygons> make_slab_loops(
|
|||||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
Polygons &loops = layers[line_idx];
|
Polygons &loops = layers[line_idx];
|
||||||
std::vector<OpenPolyline> open_polylines;
|
std::vector<OpenPolyline> open_polylines;
|
||||||
chain_lines_by_triangle_connectivity(in, loops, open_polylines);
|
chain_lines_by_triangle_connectivity<AdditionalMeshInfo::None>(in, loops, open_polylines);
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
{
|
{
|
||||||
SVG svg(debug_out_path("make_slab_loops-out-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
|
SVG svg(debug_out_path("make_slab_loops-out-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
|
||||||
@ -1574,7 +1732,7 @@ static ExPolygons make_expolygons_simple(IntersectionLines &lines)
|
|||||||
ExPolygons slices;
|
ExPolygons slices;
|
||||||
Polygons holes;
|
Polygons holes;
|
||||||
|
|
||||||
for (Polygon &loop : make_loops(lines))
|
for (Polygon &loop : make_loops<AdditionalMeshInfo::None>(lines))
|
||||||
if (loop.area() >= 0.)
|
if (loop.area() >= 0.)
|
||||||
slices.emplace_back(std::move(loop));
|
slices.emplace_back(std::move(loop));
|
||||||
else
|
else
|
||||||
@ -1745,7 +1903,8 @@ static inline bool is_identity(const Transform3d &trafo)
|
|||||||
return trafo.matrix() == Transform3d::Identity().matrix();
|
return trafo.matrix() == Transform3d::Identity().matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed_triangle_set &mesh, const Transform3d &trafo)
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
|
||||||
|
static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const typename IndexedTriangleSetType<mesh_info>::type &mesh, const Transform3d &trafo)
|
||||||
{
|
{
|
||||||
// Copy and scale vertices in XY, don't scale in Z.
|
// Copy and scale vertices in XY, don't scale in Z.
|
||||||
// Possibly apply the transformation.
|
// Possibly apply the transformation.
|
||||||
@ -1769,13 +1928,23 @@ static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Polygons> slice_mesh(
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
|
||||||
const indexed_triangle_set &mesh,
|
std::vector<typename PolygonsType<mesh_info>::type> slice_mesh(
|
||||||
|
const typename IndexedTriangleSetType<mesh_info>::type &mesh,
|
||||||
// Unscaled Zs
|
// Unscaled Zs
|
||||||
const std::vector<float> &zs,
|
const std::vector<float> &zs,
|
||||||
const MeshSlicingParams ¶ms,
|
const MeshSlicingParams ¶ms,
|
||||||
std::function<void()> throw_on_cancel)
|
std::function<void()> throw_on_cancel)
|
||||||
{
|
{
|
||||||
|
using PolygonsType = typename PolygonsType<mesh_info>::type;
|
||||||
|
|
||||||
|
const FacetColorFunctor<mesh_info> facet_color_fn = [&] {
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color)
|
||||||
|
return FacetColorFunctor<mesh_info>(mesh.colors);
|
||||||
|
else
|
||||||
|
return FacetColorFunctor<mesh_info>();
|
||||||
|
}();
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "slice_mesh to polygons";
|
BOOST_LOG_TRIVIAL(debug) << "slice_mesh to polygons";
|
||||||
|
|
||||||
std::vector<IntersectionLines> lines;
|
std::vector<IntersectionLines> lines;
|
||||||
@ -1785,29 +1954,29 @@ std::vector<Polygons> slice_mesh(
|
|||||||
// Instead of edge identifiers, one shall use a sorted pair of edge vertex indices.
|
// Instead of edge identifiers, one shall use a sorted pair of edge vertex indices.
|
||||||
// However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have
|
// However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have
|
||||||
// to make sure that no code relies on it.
|
// to make sure that no code relies on it.
|
||||||
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh);
|
std::vector<Vec3i> face_edge_ids = its_face_edge_ids<mesh_info>(mesh);
|
||||||
if (zs.size() <= 1) {
|
if (zs.size() <= 1) {
|
||||||
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
|
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
|
||||||
if (is_identity(params.trafo)) {
|
if (is_identity(params.trafo)) {
|
||||||
lines = slice_make_lines(
|
lines = slice_make_lines(
|
||||||
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); },
|
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); },
|
||||||
mesh.indices, face_edge_ids, zs, throw_on_cancel);
|
mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel);
|
||||||
} else {
|
} else {
|
||||||
// Transform the vertices, scale up in XY, not in Z.
|
// Transform the vertices, scale up in XY, not in Z.
|
||||||
Transform3f tf = make_trafo_for_slicing(params.trafo);
|
Transform3f tf = make_trafo_for_slicing(params.trafo);
|
||||||
lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel);
|
lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation.
|
// Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation.
|
||||||
lines = slice_make_lines(
|
lines = slice_make_lines(
|
||||||
transform_mesh_vertices_for_slicing(mesh, params.trafo),
|
transform_mesh_vertices_for_slicing<mesh_info>(mesh, params.trafo),
|
||||||
[](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel);
|
[](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, facet_color_fn, zs, throw_on_cancel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
|
|
||||||
std::vector<Polygons> layers = make_loops(lines, params, throw_on_cancel);
|
std::vector<PolygonsType> layers = make_loops<mesh_info>(lines, params, throw_on_cancel);
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
{
|
{
|
||||||
@ -1845,13 +2014,43 @@ std::vector<Polygons> slice_mesh(
|
|||||||
return layers;
|
return layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specialized version for a single slicing plane only, running on a single thread.
|
std::vector<Polygons> slice_mesh(
|
||||||
Polygons slice_mesh(
|
|
||||||
const indexed_triangle_set &mesh,
|
const indexed_triangle_set &mesh,
|
||||||
// Unscaled Zs
|
// Unscaled Zs
|
||||||
|
const std::vector<float> &zs,
|
||||||
|
const MeshSlicingParams ¶ms,
|
||||||
|
std::function<void()> throw_on_cancel)
|
||||||
|
{
|
||||||
|
return slice_mesh<AdditionalMeshInfo::None>(mesh, zs, params, throw_on_cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ColorPolygons> slice_mesh(
|
||||||
|
const indexed_triangle_set_with_color &mesh,
|
||||||
|
// Unscaled Zs
|
||||||
|
const std::vector<float> &zs,
|
||||||
|
const MeshSlicingParams ¶ms,
|
||||||
|
std::function<void()> throw_on_cancel)
|
||||||
|
{
|
||||||
|
return slice_mesh<AdditionalMeshInfo::Color>(mesh, zs, params, throw_on_cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized version for a single slicing plane only, running on a single thread.
|
||||||
|
template<AdditionalMeshInfo mesh_info = AdditionalMeshInfo::None>
|
||||||
|
typename PolygonsType<mesh_info>::type slice_mesh(
|
||||||
|
const typename IndexedTriangleSetType<mesh_info>::type &mesh,
|
||||||
|
// Unscaled Zs
|
||||||
const float plane_z,
|
const float plane_z,
|
||||||
const MeshSlicingParams ¶ms)
|
const MeshSlicingParams ¶ms)
|
||||||
{
|
{
|
||||||
|
using PolygonsType = typename PolygonsType<mesh_info>::type;
|
||||||
|
|
||||||
|
const FacetColorFunctor<mesh_info> facet_color_fn = [&] {
|
||||||
|
if constexpr (mesh_info == AdditionalMeshInfo::Color)
|
||||||
|
return FacetColorFunctor<mesh_info>(mesh.colors);
|
||||||
|
else
|
||||||
|
return FacetColorFunctor<mesh_info>();
|
||||||
|
}();
|
||||||
|
|
||||||
std::vector<IntersectionLines> lines;
|
std::vector<IntersectionLines> lines;
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1887,27 +2086,45 @@ Polygons slice_mesh(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3) Calculate face neighbors for just the faces in face_mask.
|
// 3) Calculate face neighbors for just the faces in face_mask.
|
||||||
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_mask);
|
std::vector<Vec3i> face_edge_ids = its_face_edge_ids<mesh_info>(mesh, face_mask);
|
||||||
|
|
||||||
// 4) Slice "face_mask" triangles, collect line segments.
|
// 4) Slice "face_mask" triangles, collect line segments.
|
||||||
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
|
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
|
||||||
if (trafo_identity) {
|
if (trafo_identity) {
|
||||||
lines.emplace_back(slice_make_lines(
|
lines.emplace_back(slice_make_lines<mesh_info>(
|
||||||
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); },
|
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); },
|
||||||
mesh.indices, face_edge_ids, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; }));
|
mesh.indices, face_edge_ids, facet_color_fn, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; }));
|
||||||
} else {
|
} else {
|
||||||
// Transform the vertices, scale up in XY, not in Z.
|
// Transform the vertices, scale up in XY, not in Z.
|
||||||
lines.emplace_back(slice_make_lines(mesh.vertices, [tf](const Vec3f& p) { return tf * p; }, mesh.indices, face_edge_ids, plane_z,
|
lines.emplace_back(slice_make_lines<mesh_info>(mesh.vertices, [tf](const Vec3f& p) { return tf * p; }, mesh.indices, face_edge_ids, facet_color_fn, plane_z,
|
||||||
[&face_mask](int face_idx) { return face_mask[face_idx]; }));
|
[&face_mask](int face_idx) { return face_mask[face_idx]; }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5) Chain the line segments.
|
// 5) Chain the line segments.
|
||||||
std::vector<Polygons> layers = make_loops(lines, params, [](){});
|
std::vector<PolygonsType> layers = make_loops<mesh_info>(lines, params, [](){});
|
||||||
assert(layers.size() == 1);
|
assert(layers.size() == 1);
|
||||||
return layers.front();
|
return layers.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygons slice_mesh(
|
||||||
|
const indexed_triangle_set &mesh,
|
||||||
|
// Unscaled Zs
|
||||||
|
const float plane_z,
|
||||||
|
const MeshSlicingParams ¶ms)
|
||||||
|
{
|
||||||
|
return slice_mesh<AdditionalMeshInfo::None>(mesh, plane_z, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorPolygons slice_mesh(
|
||||||
|
const indexed_triangle_set_with_color &mesh,
|
||||||
|
// Unscaled Zs
|
||||||
|
const float plane_z,
|
||||||
|
const MeshSlicingParams ¶ms)
|
||||||
|
{
|
||||||
|
return slice_mesh<AdditionalMeshInfo::Color>(mesh, plane_z, params);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ExPolygons> slice_mesh_ex(
|
std::vector<ExPolygons> slice_mesh_ex(
|
||||||
const indexed_triangle_set &mesh,
|
const indexed_triangle_set &mesh,
|
||||||
const std::vector<float> &zs,
|
const std::vector<float> &zs,
|
||||||
@ -2272,7 +2489,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||||||
dst.y() = scaled<double>(src.y());
|
dst.y() = scaled<double>(src.y());
|
||||||
dst.z() = src.z();
|
dst.z() = src.z();
|
||||||
}
|
}
|
||||||
slice_type = slice_facet(double(z), vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
|
slice_type = slice_facet(double(z), vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, 0, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slice_type != FacetSliceType::NoSlice) {
|
if (slice_type != FacetSliceType::NoSlice) {
|
||||||
|
@ -20,6 +20,8 @@ struct indexed_triangle_set;
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct indexed_triangle_set_with_color;
|
||||||
|
|
||||||
struct MeshSlicingParams
|
struct MeshSlicingParams
|
||||||
{
|
{
|
||||||
enum class SlicingMode : uint32_t {
|
enum class SlicingMode : uint32_t {
|
||||||
@ -37,6 +39,9 @@ struct MeshSlicingParams
|
|||||||
PositiveLargestContour,
|
PositiveLargestContour,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MeshSlicingParams() = default;
|
||||||
|
explicit MeshSlicingParams(const Transform3d &trafo) : trafo(trafo) {}
|
||||||
|
|
||||||
SlicingMode mode { SlicingMode::Regular };
|
SlicingMode mode { SlicingMode::Regular };
|
||||||
// For vase mode: below this layer a different slicing mode will be used to produce a single contour.
|
// For vase mode: below this layer a different slicing mode will be used to produce a single contour.
|
||||||
// 0 = ignore.
|
// 0 = ignore.
|
||||||
@ -75,10 +80,21 @@ std::vector<Polygons> slice_mesh(
|
|||||||
const MeshSlicingParams ¶ms,
|
const MeshSlicingParams ¶ms,
|
||||||
std::function<void()> throw_on_cancel = []{});
|
std::function<void()> throw_on_cancel = []{});
|
||||||
|
|
||||||
|
std::vector<ColorPolygons> slice_mesh(
|
||||||
|
const indexed_triangle_set_with_color &mesh,
|
||||||
|
const std::vector<float> &zs,
|
||||||
|
const MeshSlicingParams ¶ms,
|
||||||
|
std::function<void()> throw_on_cancel = []{});
|
||||||
|
|
||||||
// Specialized version for a single slicing plane only, running on a single thread.
|
// Specialized version for a single slicing plane only, running on a single thread.
|
||||||
Polygons slice_mesh(
|
Polygons slice_mesh(
|
||||||
const indexed_triangle_set &mesh,
|
const indexed_triangle_set &mesh,
|
||||||
const float plane_z,
|
float plane_z,
|
||||||
|
const MeshSlicingParams ¶ms);
|
||||||
|
|
||||||
|
ColorPolygons slice_mesh(
|
||||||
|
const indexed_triangle_set_with_color &mesh,
|
||||||
|
float plane_z,
|
||||||
const MeshSlicingParams ¶ms);
|
const MeshSlicingParams ¶ms);
|
||||||
|
|
||||||
std::vector<ExPolygons> slice_mesh_ex(
|
std::vector<ExPolygons> slice_mesh_ex(
|
||||||
|
@ -1333,11 +1333,14 @@ int TriangleSelector::num_facets(TriangleStateType state) const {
|
|||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set TriangleSelector::get_facets(TriangleStateType state) const {
|
template<AdditionalMeshInfo facet_info>
|
||||||
indexed_triangle_set out;
|
typename IndexedTriangleSetType<facet_info>::type TriangleSelector::get_facets(const std::function<bool(const Triangle &)> &facet_filter) const {
|
||||||
|
using IndexedTriangleSetType = typename IndexedTriangleSetType<facet_info>::type;
|
||||||
|
|
||||||
|
IndexedTriangleSetType out;
|
||||||
std::vector<int> vertex_map(m_vertices.size(), -1);
|
std::vector<int> vertex_map(m_vertices.size(), -1);
|
||||||
for (const Triangle &tr : m_triangles) {
|
for (const Triangle &tr : m_triangles) {
|
||||||
if (tr.valid() && ! tr.is_split() && tr.get_state() == state) {
|
if (tr.valid() && !tr.is_split() && facet_filter(tr)) {
|
||||||
stl_triangle_vertex_indices indices;
|
stl_triangle_vertex_indices indices;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
int j = tr.verts_idxs[i];
|
int j = tr.verts_idxs[i];
|
||||||
@ -1348,54 +1351,104 @@ indexed_triangle_set TriangleSelector::get_facets(TriangleStateType state) const
|
|||||||
indices[i] = vertex_map[j];
|
indices[i] = vertex_map[j];
|
||||||
}
|
}
|
||||||
out.indices.emplace_back(indices);
|
out.indices.emplace_back(indices);
|
||||||
|
|
||||||
|
if constexpr (facet_info == AdditionalMeshInfo::Color) {
|
||||||
|
out.colors.emplace_back(static_cast<uint8_t>(tr.get_state()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set TriangleSelector::get_facets_strict(TriangleStateType state) const {
|
indexed_triangle_set TriangleSelector::get_facets(TriangleStateType state) const {
|
||||||
indexed_triangle_set out;
|
return this->get_facets([state](const Triangle &tr) { return tr.get_state() == state; });
|
||||||
|
}
|
||||||
|
|
||||||
size_t num_vertices = 0;
|
indexed_triangle_set TriangleSelector::get_all_facets() const {
|
||||||
for (const Vertex &v : m_vertices)
|
return this->get_facets([](const Triangle &tr) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set_with_color TriangleSelector::get_all_facets_with_colors() const {
|
||||||
|
return this->get_facets<AdditionalMeshInfo::Color>([](const Triangle &tr) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo facet_info>
|
||||||
|
typename IndexedTriangleSetType<facet_info>::type TriangleSelector::get_facets_strict(const std::function<bool(const Triangle &)> &facet_filter) const {
|
||||||
|
using IndexedTriangleSetType = typename IndexedTriangleSetType<facet_info>::type;
|
||||||
|
|
||||||
|
auto get_vertices_count = [&vertices = std::as_const(m_vertices)]() -> size_t {
|
||||||
|
size_t vertices_cnt = 0;
|
||||||
|
for (const Vertex &v : vertices) {
|
||||||
if (v.ref_cnt > 0)
|
if (v.ref_cnt > 0)
|
||||||
++ num_vertices;
|
++vertices_cnt;
|
||||||
out.vertices.reserve(num_vertices);
|
}
|
||||||
|
|
||||||
|
return vertices_cnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexedTriangleSetType out;
|
||||||
|
out.vertices.reserve(get_vertices_count());
|
||||||
|
|
||||||
std::vector<int> vertex_map(m_vertices.size(), -1);
|
std::vector<int> vertex_map(m_vertices.size(), -1);
|
||||||
for (size_t i = 0; i < m_vertices.size(); ++ i)
|
for (size_t i = 0; i < m_vertices.size(); ++i) {
|
||||||
if (const Vertex &v = m_vertices[i]; v.ref_cnt > 0) {
|
if (const Vertex &v = m_vertices[i]; v.ref_cnt > 0) {
|
||||||
vertex_map[i] = int(out.vertices.size());
|
vertex_map[i] = int(out.vertices.size());
|
||||||
out.vertices.emplace_back(v.v);
|
out.vertices.emplace_back(v.v);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> out_colors;
|
||||||
for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle)
|
for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle)
|
||||||
this->get_facets_strict_recursive(m_triangles[itriangle], m_neighbors[itriangle], state, out.indices);
|
this->get_facets_strict_recursive<facet_info>(m_triangles[itriangle], m_neighbors[itriangle], facet_filter, out.indices, out_colors);
|
||||||
|
|
||||||
for (auto &triangle : out.indices)
|
if constexpr (facet_info == AdditionalMeshInfo::Color) {
|
||||||
for (int i = 0; i < 3; ++ i)
|
out.colors = std::move(out_colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &triangle : out.indices) {
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
triangle(i) = vertex_map[triangle(i)];
|
triangle(i) = vertex_map[triangle(i)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set TriangleSelector::get_facets_strict(TriangleStateType state) const {
|
||||||
|
return this->get_facets_strict([state](const Triangle &tr) { return tr.get_state() == state; });
|
||||||
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set TriangleSelector::get_all_facets_strict() const {
|
||||||
|
return this->get_facets_strict([](const Triangle &tr) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set_with_color TriangleSelector::get_all_facets_strict_with_colors() const {
|
||||||
|
return this->get_facets_strict<AdditionalMeshInfo::Color>([](const Triangle &tr) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo facet_info>
|
||||||
void TriangleSelector::get_facets_strict_recursive(
|
void TriangleSelector::get_facets_strict_recursive(
|
||||||
const Triangle &tr,
|
const Triangle &tr,
|
||||||
const Vec3i &neighbors,
|
const Vec3i &neighbors,
|
||||||
TriangleStateType state,
|
const std::function<bool(const Triangle &)> &facet_filter,
|
||||||
std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
std::vector<stl_triangle_vertex_indices> &out_triangles,
|
||||||
|
std::vector<uint8_t> &out_colors) const
|
||||||
{
|
{
|
||||||
if (tr.is_split()) {
|
if (tr.is_split()) {
|
||||||
for (int i = 0; i <= tr.number_of_split_sides(); ++ i)
|
for (int i = 0; i <= tr.number_of_split_sides(); ++ i)
|
||||||
this->get_facets_strict_recursive(
|
this->get_facets_strict_recursive<facet_info>(
|
||||||
m_triangles[tr.children[i]],
|
m_triangles[tr.children[i]],
|
||||||
this->child_neighbors(tr, neighbors, i),
|
this->child_neighbors(tr, neighbors, i),
|
||||||
state, out_triangles);
|
facet_filter, out_triangles, out_colors);
|
||||||
} else if (tr.get_state() == state)
|
} else if (facet_filter(tr)) {
|
||||||
this->get_facets_split_by_tjoints({tr.verts_idxs[0], tr.verts_idxs[1], tr.verts_idxs[2]}, neighbors, out_triangles);
|
const uint8_t facet_color = static_cast<uint8_t>(tr.get_state());
|
||||||
|
this->get_facets_split_by_tjoints<facet_info>({tr.verts_idxs[0], tr.verts_idxs[1], tr.verts_idxs[2]}, neighbors, facet_color, out_triangles, out_colors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
template<AdditionalMeshInfo facet_info>
|
||||||
{
|
void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, const uint8_t color, std::vector<stl_triangle_vertex_indices> &out_triangles, std::vector<uint8_t> &out_colors) const {
|
||||||
// Export this triangle, but first collect the T-joint vertices along its edges.
|
// Export this triangle, but first collect the T-joint vertices along its edges.
|
||||||
Vec3i midpoints(
|
Vec3i midpoints(
|
||||||
this->triangle_midpoint(neighbors(0), vertices(1), vertices(0)),
|
this->triangle_midpoint(neighbors(0), vertices(1), vertices(0)),
|
||||||
@ -1406,6 +1459,11 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const
|
|||||||
case 0:
|
case 0:
|
||||||
// Just emit this triangle.
|
// Just emit this triangle.
|
||||||
out_triangles.emplace_back(vertices(0), vertices(1), vertices(2));
|
out_triangles.emplace_back(vertices(0), vertices(1), vertices(2));
|
||||||
|
|
||||||
|
if constexpr (facet_info == AdditionalMeshInfo::Color) {
|
||||||
|
out_colors.emplace_back(color);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
@ -1413,18 +1471,18 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const
|
|||||||
int i = midpoints(0) != -1 ? 2 : midpoints(1) != -1 ? 0 : 1;
|
int i = midpoints(0) != -1 ? 2 : midpoints(1) != -1 ? 0 : 1;
|
||||||
int j = next_idx_modulo(i, 3);
|
int j = next_idx_modulo(i, 3);
|
||||||
int k = next_idx_modulo(j, 3);
|
int k = next_idx_modulo(j, 3);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ vertices(i), vertices(j), midpoints(j) },
|
{ vertices(i), vertices(j), midpoints(j) },
|
||||||
{ neighbors(i),
|
{ neighbors(i),
|
||||||
this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::Second),
|
this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::Second),
|
||||||
-1 },
|
-1 },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ midpoints(j), vertices(k), vertices(i) },
|
{ midpoints(j), vertices(k), vertices(i) },
|
||||||
{ this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::First),
|
{ this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::First),
|
||||||
neighbors(k),
|
neighbors(k),
|
||||||
-1 },
|
-1 },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
@ -1433,47 +1491,53 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const
|
|||||||
int i = midpoints(0) == -1 ? 2 : midpoints(1) == -1 ? 0 : 1;
|
int i = midpoints(0) == -1 ? 2 : midpoints(1) == -1 ? 0 : 1;
|
||||||
int j = next_idx_modulo(i, 3);
|
int j = next_idx_modulo(i, 3);
|
||||||
int k = next_idx_modulo(j, 3);
|
int k = next_idx_modulo(j, 3);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ vertices(i), midpoints(i), midpoints(k) },
|
{ vertices(i), midpoints(i), midpoints(k) },
|
||||||
{ this->neighbor_child(neighbors(i), vertices(j), vertices(i), Partition::Second),
|
{ this->neighbor_child(neighbors(i), vertices(j), vertices(i), Partition::Second),
|
||||||
-1,
|
-1,
|
||||||
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::First) },
|
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::First) },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ midpoints(i), vertices(j), midpoints(k) },
|
{ midpoints(i), vertices(j), midpoints(k) },
|
||||||
{ this->neighbor_child(neighbors(i), vertices(j), vertices(i), Partition::First),
|
{ this->neighbor_child(neighbors(i), vertices(j), vertices(i), Partition::First),
|
||||||
-1, -1 },
|
-1, -1 },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ vertices(j), vertices(k), midpoints(k) },
|
{ vertices(j), vertices(k), midpoints(k) },
|
||||||
{ neighbors(j),
|
{ neighbors(j),
|
||||||
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::Second),
|
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::Second),
|
||||||
-1 },
|
-1 },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
assert(splits == 3);
|
assert(splits == 3);
|
||||||
// Split to 4 triangles.
|
// Split to 4 triangles.
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ vertices(0), midpoints(0), midpoints(2) },
|
{ vertices(0), midpoints(0), midpoints(2) },
|
||||||
{ this->neighbor_child(neighbors(0), vertices(1), vertices(0), Partition::Second),
|
{ this->neighbor_child(neighbors(0), vertices(1), vertices(0), Partition::Second),
|
||||||
-1,
|
-1,
|
||||||
this->neighbor_child(neighbors(2), vertices(0), vertices(2), Partition::First) },
|
this->neighbor_child(neighbors(2), vertices(0), vertices(2), Partition::First) },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ midpoints(0), vertices(1), midpoints(1) },
|
{ midpoints(0), vertices(1), midpoints(1) },
|
||||||
{ this->neighbor_child(neighbors(0), vertices(1), vertices(0), Partition::First),
|
{ this->neighbor_child(neighbors(0), vertices(1), vertices(0), Partition::First),
|
||||||
this->neighbor_child(neighbors(1), vertices(2), vertices(1), Partition::Second),
|
this->neighbor_child(neighbors(1), vertices(2), vertices(1), Partition::Second),
|
||||||
-1 },
|
-1 },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
this->get_facets_split_by_tjoints(
|
this->get_facets_split_by_tjoints<facet_info>(
|
||||||
{ midpoints(1), vertices(2), midpoints(2) },
|
{ midpoints(1), vertices(2), midpoints(2) },
|
||||||
{ this->neighbor_child(neighbors(1), vertices(2), vertices(1), Partition::First),
|
{ this->neighbor_child(neighbors(1), vertices(2), vertices(1), Partition::First),
|
||||||
this->neighbor_child(neighbors(2), vertices(0), vertices(2), Partition::Second),
|
this->neighbor_child(neighbors(2), vertices(0), vertices(2), Partition::Second),
|
||||||
-1 },
|
-1 },
|
||||||
out_triangles);
|
color, out_triangles, out_colors);
|
||||||
|
|
||||||
out_triangles.emplace_back(midpoints);
|
out_triangles.emplace_back(midpoints);
|
||||||
|
|
||||||
|
if constexpr (facet_info == AdditionalMeshInfo::Color) {
|
||||||
|
out_colors.emplace_back(color);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1594,6 +1658,9 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
|||||||
out.data.triangles_to_split.emplace_back(i, int(out.data.bitstream.size()));
|
out.data.triangles_to_split.emplace_back(i, int(out.data.bitstream.size()));
|
||||||
// out the triangle bits.
|
// out the triangle bits.
|
||||||
out.serialize(i);
|
out.serialize(i);
|
||||||
|
} else if (!tr.is_split()) {
|
||||||
|
assert(tr.get_state() == TriangleStateType::NONE);
|
||||||
|
out.data.used_states[static_cast<int>(TriangleStateType::NONE)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// May be stored onto Undo / Redo stack, thus conserve memory.
|
// May be stored onto Undo / Redo stack, thus conserve memory.
|
||||||
|
@ -313,10 +313,26 @@ public:
|
|||||||
bool has_facets(TriangleStateType state) const;
|
bool has_facets(TriangleStateType state) const;
|
||||||
static bool has_facets(const TriangleSplittingData &data, TriangleStateType test_state);
|
static bool has_facets(const TriangleSplittingData &data, TriangleStateType test_state);
|
||||||
int num_facets(TriangleStateType state) const;
|
int num_facets(TriangleStateType state) const;
|
||||||
|
// Get facets that pass the filter. Don't triangulate T-joints.
|
||||||
|
template<AdditionalMeshInfo facet_info = AdditionalMeshInfo::None>
|
||||||
|
typename IndexedTriangleSetType<facet_info>::type get_facets(const std::function<bool(const Triangle &)> &facet_filter) const;
|
||||||
// Get facets at a given state. Don't triangulate T-joints.
|
// Get facets at a given state. Don't triangulate T-joints.
|
||||||
indexed_triangle_set get_facets(TriangleStateType state) const;
|
indexed_triangle_set get_facets(TriangleStateType state) const;
|
||||||
|
// Get all facets. Don't triangulate T-joints.
|
||||||
|
indexed_triangle_set get_all_facets() const;
|
||||||
|
// Get all facets with information about the colors of the facets. Don't triangulate T-joints.
|
||||||
|
indexed_triangle_set_with_color get_all_facets_with_colors() const;
|
||||||
|
|
||||||
|
// Get facets that pass the filter. Triangulate T-joints.
|
||||||
|
template<AdditionalMeshInfo facet_info = AdditionalMeshInfo::None>
|
||||||
|
typename IndexedTriangleSetType<facet_info>::type get_facets_strict(const std::function<bool(const Triangle &)> &facet_filter) const;
|
||||||
// Get facets at a given state. Triangulate T-joints.
|
// Get facets at a given state. Triangulate T-joints.
|
||||||
indexed_triangle_set get_facets_strict(TriangleStateType state) const;
|
indexed_triangle_set get_facets_strict(TriangleStateType state) const;
|
||||||
|
// Get all facets. Triangulate T-joints.
|
||||||
|
indexed_triangle_set get_all_facets_strict() const;
|
||||||
|
// Get all facets with information about the colord of the facetd. Triangulate T-joints.
|
||||||
|
indexed_triangle_set_with_color get_all_facets_strict_with_colors() const;
|
||||||
|
|
||||||
// Get edges around the selected area by seed fill.
|
// Get edges around the selected area by seed fill.
|
||||||
std::vector<Vec2i> get_seed_fill_contour() const;
|
std::vector<Vec2i> get_seed_fill_contour() const;
|
||||||
|
|
||||||
@ -470,12 +486,16 @@ private:
|
|||||||
bool verify_triangle_midpoints(const Triangle& tr) const;
|
bool verify_triangle_midpoints(const Triangle& tr) const;
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo facet_info>
|
||||||
void get_facets_strict_recursive(
|
void get_facets_strict_recursive(
|
||||||
const Triangle &tr,
|
const Triangle &tr,
|
||||||
const Vec3i &neighbors,
|
const Vec3i &neighbors,
|
||||||
TriangleStateType state,
|
const std::function<bool(const Triangle &)> &facet_filter,
|
||||||
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
std::vector<stl_triangle_vertex_indices> &out_triangles,
|
||||||
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
std::vector<uint8_t> &out_colors) const;
|
||||||
|
|
||||||
|
template<AdditionalMeshInfo facet_info>
|
||||||
|
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, uint8_t color, std::vector<stl_triangle_vertex_indices> &out_triangles, std::vector<uint8_t> &out_colors) const;
|
||||||
|
|
||||||
void get_seed_fill_contour_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec2i> &edges_out) const;
|
void get_seed_fill_contour_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec2i> &edges_out) const;
|
||||||
|
|
||||||
|
@ -81,14 +81,14 @@ SCENARIO("Simplify polyne, template", "[Polyline]")
|
|||||||
Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} };
|
Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} };
|
||||||
WHEN("simplified with Douglas-Peucker with back inserter") {
|
WHEN("simplified with Douglas-Peucker with back inserter") {
|
||||||
Points out;
|
Points out;
|
||||||
douglas_peucker<int64_t>(polyline.begin(), polyline.end(), std::back_inserter(out), 10, [](const Point &p) { return p; });
|
douglas_peucker<int64_t>(polyline.begin(), polyline.end(), std::back_inserter(out), 10., [](const Point &p) { return p; });
|
||||||
THEN("simplified correctly") {
|
THEN("simplified correctly") {
|
||||||
REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} });
|
REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("simplified with Douglas-Peucker in place") {
|
WHEN("simplified with Douglas-Peucker in place") {
|
||||||
Points out{ polyline };
|
Points out{ polyline };
|
||||||
out.erase(douglas_peucker<int64_t>(out.begin(), out.end(), out.begin(), 10, [](const Point &p) { return p; }), out.end());
|
out.erase(douglas_peucker<int64_t>(out.begin(), out.end(), out.begin(), 10., [](const Point &p) { return p; }), out.end());
|
||||||
THEN("simplified correctly") {
|
THEN("simplified correctly") {
|
||||||
REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} });
|
REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} });
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user