一、生成切片层类对象数组
1、SlicingParameters 切片参数
struct SlicingParameters
{
SlicingParameters() = default;
static SlicingParameters create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::vector<unsigned int> &object_extruders);
// Has any raft layers?
bool has_raft() const { return raft_layers() > 0; }
size_t raft_layers() const { return base_raft_layers + interface_raft_layers; }
// Is the 1st object layer height fixed, or could it be varied?
bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; }
// Height of the object to be printed. This value does not contain the raft height.
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
bool valid { false };
// Number of raft layers.
size_t base_raft_layers { 0 };
// Number of interface layers including the contact layer.
size_t interface_raft_layers { 0 };
// Layer heights of the raft (base, interface and a contact layer).
coordf_t base_raft_layer_height { 0 };
coordf_t interface_raft_layer_height { 0 };
coordf_t contact_raft_layer_height { 0 };
// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
// or by the variable layer thickness table.
coordf_t layer_height { 0 };
// Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
// or by an interactive layer height editor.
coordf_t min_layer_height { 0 };
coordf_t max_layer_height { 0 };
coordf_t max_suport_layer_height { 0 };
// First layer height of the print, this may be used for the first layer of the raft
// or for the first layer of the print.
coordf_t first_print_layer_height { 0 };
// Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
// or a bridging flow thickness if printed over a non-soluble raft,
// or a normal layer height if printed over a soluble raft.
coordf_t first_object_layer_height { 0 };
// If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
bool first_object_layer_bridging { false };
// Soluble interface? (PLA soluble in water, HIPS soluble in lemonen)
// otherwise the interface must be broken off.
bool soluble_interface { false };
// Gap when placing object over raft.
coordf_t gap_raft_object { 0 };
// Gap when placing support over object.
coordf_t gap_object_support { 0 };
// Gap when placing object over support.
coordf_t gap_support_object { 0 };
// Bottom and top of the printed object.
// If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
// Otherwise object_print_z_min is equal to the raft height.
coordf_t raft_base_top_z { 0 };
coordf_t raft_interface_top_z { 0 };
coordf_t raft_contact_top_z { 0 };
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
coordf_t object_print_z_min { 0 };
coordf_t object_print_z_max { 0 };
};
object_print_z_max 为模型高度,在添加模型或导入模型时,会调用PrintObject::update_slicing_parameters()函数,通过SlicingParameters::create_from_config进行赋值初始化。
2、generate_object_layers()生成对象层高数据
std::vector<coordf_t> generate_object_layers(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layer_height_profile,
bool is_precise_z_height)
{
assert(! layer_height_profile.empty());
coordf_t print_z = 0;
coordf_t height = 0;
std::vector<coordf_t> out;
if (slicing_params.first_object_layer_height_fixed()) {
out.push_back(0);
print_z = slicing_params.first_object_layer_height;
out.push_back(print_z);
}
size_t idx_layer_height_profile = 0;
// loop until we have at least one layer and the max slice_z reaches the object height
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
while (slice_z < slicing_params.object_print_z_height()) {
height = slicing_params.min_layer_height;
if (idx_layer_height_profile < layer_height_profile.size()) {
size_t next = idx_layer_height_profile + 2;
for (;;) {
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
break;
idx_layer_height_profile = next;
next += 2;
}
coordf_t z1 = layer_height_profile[idx_layer_height_profile];
coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
height = h1;
if (next < layer_height_profile.size()) {
coordf_t z2 = layer_height_profile[next];
coordf_t h2 = layer_height_profile[next + 1];
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
}
}
slice_z = print_z + 0.5 * height;
if (slice_z >= slicing_params.object_print_z_height())
break;
assert(height > slicing_params.min_layer_height - EPSILON);
assert(height < slicing_params.max_layer_height + EPSILON);
out.push_back(print_z);
print_z += height;
slice_z = print_z + 0.5 * slicing_params.min_layer_height;
out.push_back(print_z);
}
if (is_precise_z_height)
adjust_layer_series_to_align_object_height(slicing_params, out);
return out;
}
此函数会对模型进行层高的分割。将每一层的边界数值通过std::vector<coordf_t> out进行返回。以层高为0.2毫米来说,保存的值为[0,0.2,0.2,0.4,0.4,0.6...]进行返回。
3、new_layers()创建Layer类对象
LayerPtrs new_layers(
PrintObject *print_object,
// Object layers (pairs of bottom/top Z coordinate), without the raft.
const std::vector<coordf_t> &object_layers)
{
LayerPtrs out;
out.reserve(object_layers.size());
auto id = int(print_object->slicing_parameters().raft_layers());
coordf_t zmin = print_object->slicing_parameters().object_print_z_min;
Layer *prev = nullptr;
for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
Layer *layer = new Layer(id ++, print_object, hi - lo, hi + zmin, slice_z);
out.emplace_back(layer);
if (prev != nullptr) {
prev->upper_layer = layer;
layer->lower_layer = prev;
}
prev = layer;
}
return out;
}
4、Layer 切片层类
class Layer
{
public:
// Sequential index of this layer in PrintObject::m_layers, offsetted by the number of raft layers.
size_t id() const { return m_id; }
void set_id(size_t id) { m_id = id; }
PrintObject* object() { return m_object; }
const PrintObject* object() const { return m_object; }
Layer *upper_layer;
Layer *lower_layer;
bool slicing_errors;
coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
coordf_t bottom_z() const { return this->print_z - this->height; }
// BBS
mutable ExPolygons sharp_tails;
mutable ExPolygons cantilevers;
mutable std::vector<float> sharp_tails_height;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged.
// For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore
// it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer.
// These lslices aka islands are chained by the shortest traverse distance and this traversal
// order will be applied by the G-code generator to the extrusions fitting into these lslices.
// These lslices are also used to detect overhangs and overlaps between successive layers, therefore it is important
// that the 1st lslice is not compensated by the Elephant foot compensation algorithm.
ExPolygons lslices;
ExPolygons lslices_extrudable; // BBS: the extrudable part of lslices used for tree support
std::vector<BoundingBox> lslices_bboxes;
// BBS
ExPolygons loverhangs;
std::vector<std::pair<ExPolygon, int>> loverhangs_with_type;
BoundingBox loverhangs_bbox;
std::vector<LoopNode> loop_nodes;
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
LayerRegion* get_region(int idx) { return m_regions[idx]; }
LayerRegion* add_region(const PrintRegion *print_region);
const LayerRegionPtrs& regions() const { return m_regions; }
// Test whether whether there are any slices assigned to this layer.
bool empty() const;
void apply_auto_circle_compensation();
void make_slices();
// Backup and restore raw sliced regions if needed.
//FIXME Review whether not to simplify the code by keeping the raw_slices all the time.
void backup_untyped_slices();
void restore_untyped_slices();
// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
void restore_untyped_slices_no_extra_perimeters();
// Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices.
ExPolygons merged(float offset) const;
template <class T> bool any_internal_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true;
return false;
}
template <class T> bool any_bottom_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_bottom_contains(item)) return true;
return false;
}
void make_perimeters();
//BBS
void calculate_perimeter_continuity(std::vector<LoopNode> &prev_nodes);
void recrod_cooling_node_for_each_extrusion();
// Phony version of make_fills() without parameters for Perl integration only.
void make_fills() { this->make_fills(nullptr, nullptr); }
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr);
Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree,
FillAdaptive::Octree *support_fill_octree,
FillLightning::Generator* lightning_generator) const;
void make_ironing();
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
// Is there any valid extrusion assigned to this LayerRegion?
virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; }
//BBS
void simplify_wall_extrusion_path() { for (auto layerm : m_regions) layerm->simplify_wall_extrusion_entity();}
void simplify_infill_extrusion_path() { for (auto layerm : m_regions) layerm->simplify_infill_extrusion_entity(); }
//BBS: this function calculate the maximum void grid area of sparse infill of this layer. Just estimated value
coordf_t get_sparse_infill_max_void_area();
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
// If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards.
template<typename IteratorType, typename IndexType, typename FN_HIGHER_EQUAL>
static IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = 0;
}
else if (idx == IndexType(-1)) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_higher_equal(begin[idx_mid]))
idx_high = idx_mid;
else
idx_low = idx_mid;
}
idx = fn_higher_equal(begin[idx_low]) ? idx_low :
(fn_higher_equal(begin[idx_high]) ? idx_high : size);
}
else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (int(idx) < size && !fn_higher_equal(begin[idx]))
++idx;
}
return idx;
}
size_t get_extruder_id(unsigned int filament_id) const;
protected:
friend class PrintObject;
friend std::vector<Layer*> new_layers(PrintObject*, const std::vector<coordf_t>&);
friend std::string fix_slicing_errors(PrintObject* object, LayerPtrs&, const std::function<void()>&, int &);
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false),
slice_z(slice_z), print_z(print_z), height(height),
m_id(id), m_object(object) {}
virtual ~Layer();
//BBS: method to simplify support path
void simplify_support_entity_collection(ExtrusionEntityCollection* entity_collection);
void simplify_support_path(ExtrusionPath* path);
void simplify_support_multi_path(ExtrusionMultiPath* multipath);
void simplify_support_loop(ExtrusionLoop* loop);
private:
// Sequential index of layer, 0-based, offsetted by number of raft layers.
size_t m_id;
PrintObject *m_object;
LayerRegionPtrs m_regions;
};
成员变量:
m_id:从0开始计数,进行累加。
height:层高
print_z:开始打印的z坐标
slice_z:切片的z坐标,层最高最低边界位置的中间值
通过new_layers得到的指针数组,保存在PrintObject::m_layers中,在PrintObject::slice函数中被调用。
二、切片体积
1、多边形
1.1 Vec2crd 2维坐标向量
using coord_t = int32_t;
using coordf_t = double;
using Vec2crd = Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign>;
1.2 Point 坐标点
class Point : public Vec2crd
{
public:
using coord_type = coord_t;
Point() : Vec2crd(0, 0) {}
Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
Point(int64_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
Point(double x, double y) : Vec2crd(coord_t(lrint(x)), coord_t(lrint(y))) {}
Point(const Point &rhs) { *this = rhs; }
explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(lrint(rhs.x())), coord_t(lrint(rhs.y()))) {}
// This constructor allows you to construct Point from Eigen expressions
template<typename OtherDerived>
Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
static Point new_scale(const Vec2d &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
static Point new_scale(const Vec2f &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
// This method allows you to assign Eigen expressions to MyVectorType
template<typename OtherDerived>
Point& operator=(const Eigen::MatrixBase<OtherDerived> &other)
{
this->Vec2crd::operator=(other);
return *this;
}
Point& operator+=(const Point& rhs) { this->x() += rhs.x(); this->y() += rhs.y(); return *this; }
Point& operator-=(const Point& rhs) { this->x() -= rhs.x(); this->y() -= rhs.y(); return *this; }
Point& operator*=(const double &rhs) { this->x() = coord_t(this->x() * rhs); this->y() = coord_t(this->y() * rhs); return *this; }
Point operator*(const double &rhs) { return Point(this->x() * rhs, this->y() * rhs); }
bool both_comp(const Point &rhs, const std::string& op) {
if (op == ">")
return this->x() > rhs.x() && this->y() > rhs.y();
else if (op == "<")
return this->x() < rhs.x() && this->y() < rhs.y();
return false;
}
bool any_comp(const Point &rhs, const std::string &op)
{
if (op == ">")
return this->x() > rhs.x() || this->y() > rhs.y();
else if (op == "<")
return this->x() < rhs.x() || this->y() < rhs.y();
return false;
}
bool any_comp(const coord_t val, const std::string &op)
{
if (op == ">")
return this->x() > val || this->y() > val;
else if (op == "<")
return this->x() < val || this->y() < val;
return false;
}
void rotate(double angle) { this->rotate(std::cos(angle), std::sin(angle)); }
void rotate(double cos_a, double sin_a) {
double cur_x = (double)this->x();
double cur_y = (double)this->y();
this->x() = (coord_t)round(cos_a * cur_x - sin_a * cur_y);
this->y() = (coord_t)round(cos_a * cur_y + sin_a * cur_x);
}
void rotate(double angle, const Point ¢er);
Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; }
Point rotated(double cos_a, double sin_a) const { Point res(*this); res.rotate(cos_a, sin_a); return res; }
Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; }
Point rotate_90_degree_ccw() const { return Point(-this->y(), this->x()); }
int nearest_point_index(const Points &points) const;
int nearest_point_index(const PointConstPtrs &points) const;
int nearest_point_index(const PointPtrs &points) const;
bool nearest_point(const Points &points, Point* point) const;
double ccw(const Point &p1, const Point &p2) const;
double ccw(const Line &line) const;
double ccw_angle(const Point &p1, const Point &p2) const;
Point projection_onto(const MultiPoint &poly) const;
Point projection_onto(const Line &line) const;
bool is_in_lines(const Points &pts) const;
};
inline bool operator<(const Point &l, const Point &r)
{
return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y());
}
inline Point operator* (const Point& l, const double& r)
{
return { coord_t(l.x() * r), coord_t(l.y() * r) };
}
inline std::ostream &operator<<(std::ostream &os, const Point &pt)
{
os << unscale_(pt.x()) << "," << unscale_(pt.y());
return os;
}
1.3 MultiPoint多点
class MultiPoint
{
public:
Points points;
MultiPoint() {}
MultiPoint(const MultiPoint &other) : points(other.points) {}
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
MultiPoint(std::initializer_list<Point> list) : points(list) {}
explicit MultiPoint(const Points &_points) : points(_points) {}
MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; }
MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; }
void scale(double factor);
void scale(double factor_x, double factor_y);
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
void translate(const Point &vector);
void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
void rotate(double cos_angle, double sin_angle);
void rotate(double angle, const Point ¢er);
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
const Point& front() const { return this->points.front(); }
const Point& back() const { return this->points.back(); }
const Point& first_point() const { return this->front(); }
virtual const Point& last_point() const = 0;
virtual Lines lines() const = 0;
size_t size() const { return points.size(); }
bool empty() const { return points.empty(); }
double length() const;
bool is_valid() const { return this->points.size() >= 2; }
// Return index of a polygon point exactly equal to point.
// Return -1 if no such point exists.
int find_point(const Point &point) const;
// Return index of the closest point to point closer than scaled_epsilon.
// Return -1 if no such point exists.
int find_point(const Point &point, const double scaled_epsilon) const;
bool has_boundary_point(const Point &point) const;
int closest_point_index(const Point &point) const {
int idx = -1;
if (! this->points.empty()) {
idx = 0;
double dist_min = (point - this->points.front()).cast<double>().norm();
for (int i = 1; i < int(this->points.size()); ++ i) {
double d = (this->points[i] - point).cast<double>().norm();
if (d < dist_min) {
dist_min = d;
idx = i;
}
}
}
return idx;
}
const Point* closest_point(const Point &point) const { return this->points.empty() ? nullptr : &this->points[this->closest_point_index(point)]; }
// The distance of polygon to point is defined as:
// the minimum distance of all points to that point
double distance_to(const Point& point) const {
const Point* cl = closest_point(point);
return (*cl - point).cast<double>().norm();
}
BoundingBox bounding_box() const;
// Return true if there are exact duplicates.
bool has_duplicate_points() const;
// Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points();
bool remove_colinear_points();
void clear() { this->points.clear(); }
void append(const Point &point) { this->points.push_back(point); }
void append(const Points &src) { this->append(src.begin(), src.end()); }
void append(const Points::const_iterator &begin, const Points::const_iterator &end) { this->points.insert(this->points.end(), begin, end); }
void append(Points &&src)
{
if (this->points.empty()) {
this->points = std::move(src);
} else {
this->points.insert(this->points.end(), src.begin(), src.end());
src.clear();
}
}
bool intersection(const Line& line, Point* intersection) const;
bool first_intersection(const Line& line, Point* intersection) const;
bool intersections(const Line &line, Points *intersections) const;
void symmetric_y(const coord_t &y_axis);
static Points _douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double tolerance);
static Points concave_hull_2d(const Points& pts, const double tolerence);
inline auto begin() { return points.begin(); }
inline auto begin() const { return points.begin(); }
inline auto end() { return points.end(); }
inline auto end() const { return points.end(); }
inline auto cbegin() const { return points.begin(); }
inline auto cend() const { return points.end(); }
};
1.4 多段线
class Polyline;
class ThickPolyline;
typedef std::vector<Polyline> Polylines;
typedef std::vector<ThickPolyline> ThickPolylines;
class Polyline : public MultiPoint {
public:
Polyline() {};
Polyline(const Polyline& other) : MultiPoint(other.points), fitting_result(other.fitting_result) {}
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)), fitting_result(std::move(other.fitting_result)) {}
Polyline(std::initializer_list<Point> list) : MultiPoint(list) {
fitting_result.clear();
}
explicit Polyline(const Point &p1, const Point &p2) {
points.reserve(2);
points.emplace_back(p1);
points.emplace_back(p2);
fitting_result.clear();
}
explicit Polyline(const Points &points) : MultiPoint(points) {
fitting_result.clear();
}
explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {
fitting_result.clear();
}
Polyline& operator=(const Polyline& other) {
points = other.points;
fitting_result = other.fitting_result;
return *this;
}
Polyline& operator=(Polyline&& other) {
points = std::move(other.points);
fitting_result = std::move(other.fitting_result);
return *this;
}
static Polyline new_scale(const std::vector<Vec2d> &points) {
Polyline pl;
pl.points.reserve(points.size());
for (const Vec2d &pt : points)
pl.points.emplace_back(Point::new_scale(pt(0), pt(1)));
//BBS: new_scale doesn't support arc, so clean
pl.fitting_result.clear();
return pl;
}
void append(const Point &point) {
//BBS: don't need to append same point
if (!this->empty() && this->last_point() == point)
return;
MultiPoint::append(point);
append_fitting_result_after_append_points();
}
void append_before(const Point& point) {
//BBS: don't need to append same point
if (!this->empty() && this->first_point() == point)
return;
if (this->size() == 1) {
this->fitting_result.clear();
MultiPoint::append(point);
MultiPoint::reverse();
} else {
this->reverse();
this->append(point);
this->reverse();
}
}
void append(const Points &src) {
//BBS: don't need to append same point
if (!this->empty() && !src.empty() && this->last_point() == src[0])
this->append(src.begin() + 1, src.end());
else
this->append(src.begin(), src.end());
}
void append(const Points::const_iterator &begin, const Points::const_iterator &end) {
//BBS: don't need to append same point
if (!this->empty() && begin != end && this->last_point() == *begin)
MultiPoint::append(begin + 1, end);
else
MultiPoint::append(begin, end);
append_fitting_result_after_append_points();
}
void append(Points &&src)
{
MultiPoint::append(std::move(src));
append_fitting_result_after_append_points();
}
void append(const Polyline& src);
void append(Polyline&& src);
Polyline rebase_at(size_t idx);
Point& operator[](Points::size_type idx) { return this->points[idx]; }
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
const Point& last_point() const override { return this->points.back(); }
const Point& leftmost_point() const;
Lines lines() const override;
void clear() { MultiPoint::clear(); this->fitting_result.clear(); }
void reverse();
void clip_end(double distance);
void clip_start(double distance);
void extend_end(double distance);
void extend_start(double distance);
Points equally_spaced_points(double distance) const;
void simplify(double tolerance);
// template <class T> void simplify_by_visibility(const T &area);
void split_at(Point &point, Polyline* p1, Polyline* p2) const;
bool split_at_index(const size_t index, Polyline* p1, Polyline* p2) const;
bool split_at_length(const double length, Polyline *p1, Polyline *p2) const;
bool is_straight() const;
bool is_closed() const { return this->points.front() == this->points.back(); }
//BBS: store arc fitting result
std::vector<PathFittingData> fitting_result;
//BBS: simplify points by arc fitting
void simplify_by_fitting_arc(double tolerance);
//BBS:
Polylines equally_spaced_lines(double distance) const;
private:
void append_fitting_result_after_append_points();
void append_fitting_result_after_append_polyline(const Polyline& src);
void reset_to_linear_move();
bool split_fitting_result_before_index(const size_t index, Point &new_endpoint, std::vector<PathFittingData>& data) const;
bool split_fitting_result_after_index(const size_t index, Point &new_startpoint, std::vector<PathFittingData>& data) const;
};
1.5 Line 线
class Line
{
public:
Line() {}
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
void scale(double factor) { this->a *= factor; this->b *= factor; }
void translate(const Point &v) { this->a += v; this->b += v; }
void translate(double x, double y) { this->translate(Point(x, y)); }
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); }
double length() const { return (b - a).cast<double>().norm(); }
Point midpoint() const { return (this->a + this->b) / 2; }
bool intersection_infinite(const Line &other, Point* point) const;
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, Point *closest_point) const { return line_alg::distance_to_squared(*this, point, closest_point); }
double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); }
double distance_to_infinite_squared(const Point &point, Point *closest_point) const { return line_alg::distance_to_infinite_squared(*this, point, closest_point); }
double perp_distance_to(const Point &point) const;
bool parallel_to(double angle) const;
bool parallel_to(const Line& line) const;
bool perpendicular_to(double angle) const;
bool perpendicular_to(const Line& line) const;
double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); }
double orientation() const;
double direction() const;
Vector vector() const { return this->b - this->a; }
Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); }
bool intersection(const Line& line, Point* intersection) const;
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
bool clip_with_bbox(const BoundingBox &bbox);
// Extend the line from both sides by an offset.
void extend(double offset);
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
// Returns a distance to the closest point on the infinite.
// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
static inline double distance_to_infinite_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_infinite_squared(Line{a, b}, Vec<2, coord_t>{point}); }
static double distance_to_infinite(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_infinite_squared(point, a, b)); }
Point a;
Point b;
static const constexpr int Dim = 2;
using Scalar = Point::Scalar;
};
1.5.1 IntersectionLine 交叉线
class IntersectionLine : public Line
{
public:
IntersectionLine() = default;
bool skip() const { return (this->flags & SKIP) != 0; }
void set_skip() { this->flags |= SKIP; }
bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
void reverse() { std::swap(a, b); std::swap(a_id, b_id); std::swap(edge_a_id, edge_b_id); }
// Inherits Point a, b
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
// Vertex indices of the line end points.
int a_id { -1 };
int b_id { -1 };
// Source mesh edges of the line end points.
int edge_a_id { -1 };
int edge_b_id { -1 };
enum class FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges.
General,
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
Top,
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
Bottom,
// Two vertices are aligned with the cutting plane, the edge is shared by two triangles, where one
// triangle is below or at the cutting plane and the other is above or at the cutting plane (only one
// vertex may lie on the plane).
TopBottom,
// All three vertices of a face are aligned with the cutting plane.
Horizontal,
// Edge
Slab,
};
// feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type { FacetEdgeType::General };
// Used to skip duplicate edges.
enum {
// Triangle edge added, because it has no neighbor.
EDGE0_NO_NEIGHBOR = 0x001,
EDGE1_NO_NEIGHBOR = 0x002,
EDGE2_NO_NEIGHBOR = 0x004,
// Triangle edge added, because it makes a fold with another horizontal edge.
EDGE0_FOLD = 0x010,
EDGE1_FOLD = 0x020,
EDGE2_FOLD = 0x040,
// The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
NO_SEED = 0x100,
SKIP = 0x200,
};
uint32_t flags { 0 };
#ifndef NDEBUG
enum class Source {
BottomPlane,
TopPlane,
Slab,
};
Source source { Source::BottomPlane };
#endif // NDEBUG
};
1.6 Polygon 多边形
class Polygon;
using Polygons = std::vector<Polygon>;
using PolygonPtrs = std::vector<Polygon*>;
using ConstPolygonPtrs = std::vector<const Polygon*>;
// 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 Polygons& polygons, const Point& p, bool border_result = true);
class Polygon : public MultiPoint
{
public:
Polygon() = default;
explicit Polygon(const Points &points) : MultiPoint(points) {}
Polygon(std::initializer_list<Point> points) : MultiPoint(points) {}
Polygon(const Polygon &other) : MultiPoint(other.points) {}
Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {}
static Polygon new_scale(const std::vector<Vec2d> &points) {
Polygon pgn;
pgn.points.reserve(points.size());
for (const Vec2d &pt : points)
pgn.points.emplace_back(Point::new_scale(pt(0), pt(1)));
return pgn;
}
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
Point& operator[](Points::size_type idx) { return this->points[idx]; }
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
// last point == first point for polygons
const Point& last_point() const { return this->points.front(); }
double length() const;
Lines lines() const;
Polyline split_at_vertex(const Point &point) const;
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
Polyline split_at_index(int index) const;
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
Polyline split_at_first_point() const { return this->split_at_index(0); }
Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); }
static double area(const Points &pts);
double area() const;
bool is_counter_clockwise() const;
bool is_clockwise() const;
bool make_counter_clockwise();
bool make_clockwise();
bool is_valid() const { return this->points.size() >= 3; }
void douglas_peucker(double tolerance);
// Point ¢er : out, the center of circle
// double &diameter: out, the diameter of circle
bool is_approx_circle(double max_deviation, double max_variance, Point ¢er, double &diameter) const;
// Does an unoriented polygon contain a point?
bool contains(const Point &point) const { return Slic3r::contains(*this, point, true); }
// Approximate on boundary test.
bool on_boundary(const Point &point, double eps) const
{ return (this->point_projection(point) - point).cast<double>().squaredNorm() < eps * eps; }
// Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()!
Polygons simplify(double tolerance) const;
void densify(float min_length, std::vector<float>* lengths = nullptr);
void triangulate_convex(Polygons* polygons) const;
Point centroid() const;
bool intersection(const Line& line, Point* intersection) const;
bool first_intersection(const Line& line, Point* intersection) const;
bool intersections(const Line& line, Points* intersections) const;
bool overlaps(const Polygons& other) const;
// Considering CCW orientation of this polygon, find all convex resp. concave points
// with the angle at the vertex larger than a threshold.
// Zero angle_threshold means to accept all convex resp. concave points.
Points convex_points(double angle_threshold = 0.) const;
Points concave_points(double angle_threshold = 0.) const;
// Projection of a point onto the polygon.
Point point_projection(const Point &point) const;
std::vector<float> parameter_by_length() const;
//BBS
Polygon transform(const Transform3d& trafo) const;
using iterator = Points::iterator;
using const_iterator = Points::const_iterator;
};
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
BoundingBox get_extents(const Polygon &poly);
BoundingBox get_extents(const Polygons &polygons);
BoundingBox get_extents_rotated(const Polygon &poly, double angle);
BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
1.6 ExPolygon 多边形扩展
class ExPolygon;
using ExPolygons = std::vector<ExPolygon>;
class ExPolygon
{
public:
ExPolygon() = default;
ExPolygon(const ExPolygon &other) = default;
ExPolygon(ExPolygon &&other) = default;
explicit ExPolygon(const Polygon &contour) : contour(contour) {}
explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {}
explicit ExPolygon(const Points &contour) : contour(contour) {}
explicit ExPolygon(Points &&contour) : contour(std::move(contour)) {}
explicit ExPolygon(const Polygon &contour, const Polygon &hole) : contour(contour) { holes.emplace_back(hole); }
explicit ExPolygon(Polygon &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); }
explicit ExPolygon(const Points &contour, const Points &hole) : contour(contour) { holes.emplace_back(hole); }
explicit ExPolygon(Points &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); }
ExPolygon(std::initializer_list<Point> contour) : contour(contour) {}
ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {}
ExPolygon& operator=(const ExPolygon &other) = default;
ExPolygon& operator=(ExPolygon &&other) = default;
Polygon contour; //CCW
Polygons holes; //CW
void clear() { contour.points.clear(); holes.clear(); }
void scale(double factor);
void scale(double factor_x, double factor_y);
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
void translate(const Point &vector);
void rotate(double angle);
void rotate(double angle, const Point ¢er);
double area() const;
bool empty() const { return contour.points.empty(); }
bool is_valid() const;
void douglas_peucker(double tolerance);
// Contains the line / polyline / polylines etc COMPLETELY.
bool contains(const Line &line) const;
bool contains(const Polyline &polyline) const;
bool contains(const Polylines &polylines) const;
bool contains(const Point &point, bool border_result = true) const;
// Approximate on boundary test.
bool on_boundary(const Point &point, double eps) const;
// Projection of a point onto the polygon.
Point point_projection(const Point &point) const;
void symmetric_y(const coord_t &y_axis);
// Does this expolygon overlap another expolygon?
// Either the ExPolygons intersect, or one is fully inside the other,
// and it is not inside a hole of the other expolygon.
// The test may not be commutative if the two expolygons touch by a boundary only,
// see unit test SCENARIO("Clipper diff with polyline", "[Clipper]").
// Namely expolygons touching at a vertical boundary are considered overlapping, while expolygons touching
// at a horizontal boundary are NOT considered overlapping.
bool overlaps(const ExPolygon &other) const;
void simplify_p(double tolerance, Polygons* polygons) const;
Polygons simplify_p(double tolerance) const;
ExPolygons simplify(double tolerance) const;
void simplify(double tolerance, ExPolygons* expolygons) const;
void medial_axis(double min_width, double max_width, ThickPolylines* polylines) const;
void medial_axis(double min_width, double max_width, Polylines* polylines) const;
Polylines medial_axis(double min_width, double max_width) const
{ Polylines out; this->medial_axis(min_width, max_width, &out); return out; }
Lines lines() const;
bool remove_colinear_points();
// Number of contours (outer contour with holes).
size_t num_contours() const { return this->holes.size() + 1; }
Polygon& contour_or_hole(size_t idx) { return (idx == 0) ? this->contour : this->holes[idx - 1]; }
const Polygon& contour_or_hole(size_t idx) const { return (idx == 0) ? this->contour : this->holes[idx - 1]; }
//split expolygon-support with holes to help remove
ExPolygons split_expoly_with_holes(coord_t gap_width, const ExPolygons& collision) const;
};
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
2、生成切片层与所有模型的每层交叉线
2.1 create_edge_map 创建边映射
// Create a mapping from triangle edge into face.
struct EdgeToFace {
// Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
int vertex_low;
// Index of the 2nd vertex of the triangle edge.
int vertex_high;
// Index of a triangular face.
int face;
// Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
int face_edge;
bool operator==(const EdgeToFace &other) const { return 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>
static std::vector<EdgeToFace> create_edge_map(
const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
{
std::vector<EdgeToFace> edges_map;
edges_map.reserve(its.indices.size() * 3);
for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx)
if (face_filter(facet_idx))
for (int i = 0; i < 3; ++ i) {
edges_map.push_back({});
EdgeToFace &e2f = edges_map.back();
e2f.vertex_low = its.indices[facet_idx][i];
e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
e2f.face = facet_idx;
// 1 based indexing, to be always strictly positive.
e2f.face_edge = i + 1;
if (e2f.vertex_low > e2f.vertex_high) {
// Sort the vertices
std::swap(e2f.vertex_low, e2f.vertex_high);
// and make the face_edge negative to indicate a flipped edge.
e2f.face_edge = - e2f.face_edge;
}
}
throw_on_cancel();
std::sort(edges_map.begin(), edges_map.end());
return edges_map;
}
轮询三角面片,将顶点的三条边保存成3个EdgeToFace对象写入edges_map中;三角面片边的起点写入vertex_low,终点写入vertex_high,面片索引写入face,边号写入face_edge中;如果起点大于终点,则进行互换且face_edge变成负face_edge。
edges_map按起点 起点相同按终点进行从小到大排序
2.2 its_face_edge_ids_impl 面边索引
template<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)
{
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);
// Assign a unique common edge id to touching triangle edges.
int num_edges = 0;
for (size_t i = 0; i < edges_map.size(); ++ i) {
EdgeToFace &edge_i = edges_map[i];
if (edge_i.face == -1)
// This edge has been connected to some neighbor already.
continue;
// Unconnected edge. Find its neighbor with the correct orientation.
size_t j;
bool found = false;
for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
if (edge_i.face_edge * edges_map[j].face_edge < 0 && edges_map[j].face != -1) {
// Faces touching with opposite oriented edges and none of the edges is connected yet.
found = true;
break;
}
if (! found) {
//FIXME Vojtech: Trying to find an edge with equal orientation. This smells.
// admesh can assign the same edge ID to more than two facets (which is
// still topologically correct), so we have to search for a duplicate of
// this edge too in case it was already seen in this orientation
for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
if (edges_map[j].face != -1) {
// Faces touching with equally oriented edges and none of the edges is connected yet.
found = true;
break;
}
}
// Assign an edge index to the 1st face.
out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges;
if (found) {
EdgeToFace &edge_j = edges_map[j];
out[edge_j.face](std::abs(edge_j.face_edge) - 1) = num_edges;
// Mark the edge as connected.
edge_j.face = -1;
}
++ num_edges;
if ((i & 0x0ffff) == 0)
throw_on_cancel();
}
return out;
}
创建边映射后,进行轮询边映射,然后向后查找相同边的面。将当前面的边写入编号num_edges,如果找到要同边的面,将找到的面的边也写入编号num_edges;数组out的类型为Vec3i,三角面片的三条边的num_edges就写入对应的位置。
2.3 transform_mesh_vertices_for_slicing 网格顶点变换
static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed_triangle_set &mesh, const Transform3d &trafo)
{
// Copy and scale vertices in XY, don't scale in Z.
// Possibly apply the transformation.
static constexpr const double s = 1. / SCALING_FACTOR;
std::vector<stl_vertex> out(mesh.vertices);
if (is_identity(trafo)) {
// Identity.
for (stl_vertex &v : out) {
// Scale just XY, leave Z unscaled.
v.x() *= float(s);
v.y() *= float(s);
}
} else {
// Transform the vertices, scale up in XY, not in Y.
auto t = trafo;
t.prescale(Vec3d(s, s, 1.));
auto tf = t.cast<float>();
for (stl_vertex &v : out)
v = tf * v;
}
return out;
}
将网格的顶点坐标放大100000倍。
2.4 slice_facet 以单个层高面切片单个三角面
static FacetSliceType slice_facet(
// Z height of the slice in XY plane. Scaled or unscaled (same as vertices[].z()).
float slice_z,
// 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
const stl_vertex *vertices,
const stl_triangle_vertex_indices &indices,
const Vec3i &edge_ids,
const int idx_vertex_lowest,
const bool horizontal,
IntersectionLine &line_out)
{
IntersectionPoint points[3];
size_t num_points = 0;
auto point_on_layer = size_t(-1);
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
for (int j = 0; j < 3; ++ j) { // loop through facet edges
int edge_id;
const stl_vertex *a, *b, *c;
int a_id, b_id;
{
int k = (idx_vertex_lowest + j) % 3;
int l = (k + 1) % 3;
edge_id = edge_ids(k);
a_id = indices[k];
a = vertices + k;
b_id = indices[l];
b = vertices + l;
c = vertices + (k + 2) % 3;
}
// Is edge or face aligned with the cutting plane?
if (a->z() == slice_z && b->z() == slice_z) {
// Edge is horizontal and belongs to the current layer.
// The following rotation of the three vertices may not be efficient, but this branch happens rarely.
const stl_vertex &v0 = vertices[0];
const stl_vertex &v1 = vertices[1];
const stl_vertex &v2 = vertices[2];
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = FacetSliceType::Slicing;
if (horizontal) {
// All three vertices are aligned with slice_z.
line_out.edge_type = IntersectionLine::FacetEdgeType::Horizontal;
result = FacetSliceType::Cutting;
double normal = (v1.x() - v0.x()) * (v2.y() - v1.y()) - (v1.y() - v0.y()) * (v2.x() - v1.x());
if (normal < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b);
std::swap(a_id, b_id);
}
} else {
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
// Is the third vertex below the cutting plane?
bool third_below = v0.z() < slice_z || v1.z() < slice_z || v2.z() < slice_z;
// Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice
// only if it is the upper edge.
// (the bottom most edge resp. vertex of a triangle is not owned by the triangle, but the top most edge resp. vertex is part of the triangle
// in respect to the cutting plane).
result = third_below ? FacetSliceType::Slicing : FacetSliceType::Cutting;
if (third_below) {
line_out.edge_type = IntersectionLine::FacetEdgeType::Top;
std::swap(a, b);
std::swap(a_id, b_id);
} else
line_out.edge_type = IntersectionLine::FacetEdgeType::Bottom;
}
line_out.a.x() = a->x();
line_out.a.y() = a->y();
line_out.b.x() = b->x();
line_out.b.y() = b->y();
line_out.a_id = a_id;
line_out.b_id = b_id;
assert(line_out.a != line_out.b);
return result;
}
if (a->z() == slice_z) {
// Only point a alings with the cutting plane.
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points ++];
point.x() = a->x();
point.y() = a->y();
point.point_id = a_id;
}
} else if (b->z() == slice_z) {
// Only point b alings with the cutting plane.
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points ++];
point.x() = b->x();
point.y() = b->y();
point.point_id = b_id;
}
} else if ((a->z() < slice_z && b->z() > slice_z) || (b->z() < slice_z && a->z() > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
assert(a_id != b_id);
// Sort the edge to give a consistent answer.
if (a_id > b_id) {
std::swap(a_id, b_id);
std::swap(a, b);
}
IntersectionPoint &point = points[num_points];
double t = (double(slice_z) - double(b->z())) / (double(a->z()) - double(b->z()));
if (t <= 0.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point.x() = a->x();
point.y() = a->y();
point_on_layer = num_points ++;
point.point_id = a_id;
}
} else if (t >= 1.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point.x() = b->x();
point.y() = b->y();
point_on_layer = num_points ++;
point.point_id = b_id;
}
} else {
point.x() = coord_t(floor(double(b->x()) + (double(a->x()) - double(b->x())) * t + 0.5));
point.y() = coord_t(floor(double(b->y()) + (double(a->y()) - double(b->y())) * t + 0.5));
point.edge_id = edge_id;
++ num_points;
}
}
}
// Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
assert(num_points < 3);
if (num_points == 2) {
line_out.edge_type = IntersectionLine::FacetEdgeType::General;
line_out.a = static_cast<const Point&>(points[1]);
line_out.b = static_cast<const Point&>(points[0]);
line_out.a_id = points[1].point_id;
line_out.b_id = points[0].point_id;
line_out.edge_a_id = points[1].edge_id;
line_out.edge_b_id = points[0].edge_id;
// Not a zero lenght edge.
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//assert(line_out.a != line_out.b);
// The plane cuts at least one edge in a general position.
assert(line_out.a_id == -1 || line_out.b_id == -1);
assert(line_out.edge_a_id != -1 || line_out.edge_b_id != -1);
// General slicing position, use the segment for both slicing and object cutting.
#if 0
if (line_out.a_id != -1 && line_out.b_id != -1) {
// Solving a degenerate case, where both the intersections snapped to an edge.
// Correctly classify the face as below or above based on the position of the 3rd point.
int i = indices[0];
if (i == line_out.a_id || i == line_out.b_id)
i = indices[1];
if (i == line_out.a_id || i == line_out.b_id)
i = indices[2];
assert(i != line_out.a_id && i != line_out.b_id);
line_out.edge_type = ((m_use_quaternion ?
(m_quaternion * this->v_scaled_shared[i]).z()
: this->v_scaled_shared[i].z()) < slice_z) ? IntersectionLine::FacetEdgeType::Top : IntersectionLine::FacetEdgeType::Bottom;
}
#endif
return FacetSliceType::Slicing;
}
return FacetSliceType::NoSlice;
}
计算三角面片与切面的交叉线,通过三个点的z轴高度与切面z轴高度比较,判断有两个点在切面上面或下面,计算切点位置,保存在line_out的a,b变量中。
2.5 slice_facet_at_zs 以所有层高面切片单个三角面
template<typename TransformVertex>
void slice_facet_at_zs(
// Scaled or unscaled vertices. transform_vertex_fn may scale zs.
const std::vector<Vec3f> &mesh_vertices,
const TransformVertex &transform_vertex_fn,
const stl_triangle_vertex_indices &indices,
const Vec3i &edge_ids,
// 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,
std::vector<IntersectionLines> &lines,
std::array<std::mutex, 64> &lines_mutex)
{
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)]) };
// find facet extents
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
// find layer extents
auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il;
// 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) {
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
size_t slice_id = it - zs.begin();
boost::lock_guard<std::mutex> l(lines_mutex[slice_id % lines_mutex.size()]);
lines[slice_id].emplace_back(il);
}
}
}
计算三角面片的最高最低层高位置,轮询层高位置,将切片出来的交叉线IntersectionLine il,保存到数组lines中。
2.6 slice_make_lines 切片所有三角面片
template<typename TransformVertex, typename ThrowOnCancel>
static inline std::vector<IntersectionLines> slice_make_lines(
const std::vector<stl_vertex> &vertices,
const TransformVertex &transform_vertex_fn,
const std::vector<stl_triangle_vertex_indices> &indices,
const std::vector<Vec3i> &face_edge_ids,
const std::vector<float> &zs,
const ThrowOnCancel throw_on_cancel_fn)
{
std::vector<IntersectionLines> lines(zs.size(), IntersectionLines());
std::array<std::mutex, 64> lines_mutex;
tbb::parallel_for(
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) {
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
if ((face_idx & 0x0ffff) == 0)
throw_on_cancel_fn();
slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], zs, lines, lines_mutex);
}
}
);
return lines;
}
使用tbb并行切片所有三角面片,同一层调交叉线保存在同一索引的交叉线组中。
3、将每一层的交叉线连接相邻线段,形成闭环或多线段
3.1 chain_lines_by_triangle_connectivity
static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
{
// Build a map of lines by edge_a_id and a_id.
std::vector<IntersectionLine*> by_edge_a_id;
std::vector<IntersectionLine*> by_a_id;
by_edge_a_id.reserve(lines.size());
by_a_id.reserve(lines.size());
for (IntersectionLine &line : lines) {
if (! line.skip()) {
if (line.edge_a_id != -1)
by_edge_a_id.emplace_back(&line);
if (line.a_id != -1)
by_a_id.emplace_back(&line);
}
}
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
// Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
IntersectionLines::iterator it_line_seed = lines.begin();
for (;;) {
// take first spare line and start a new loop
IntersectionLine *first_line = nullptr;
for (; it_line_seed != lines.end(); ++ it_line_seed)
if (it_line_seed->is_seed_candidate()) {
//if (! it_line_seed->skip()) {
first_line = &(*it_line_seed ++);
break;
}
if (first_line == nullptr)
break;
first_line->set_skip();
Points loop_pts;
loop_pts.emplace_back(first_line->a);
IntersectionLine *last_line = first_line;
/*
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
*/
IntersectionLine key;
for (;;) {
// find a line starting where last one finishes
IntersectionLine* next_line = nullptr;
if (last_line->edge_b_id != -1) {
key.edge_a_id = last_line->edge_b_id;
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
if (it_begin != by_edge_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip()) {
next_line = *it_line;
break;
}
}
}
if (next_line == nullptr && last_line->b_id != -1) {
key.a_id = last_line->b_id;
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
if (it_begin != by_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip()) {
next_line = *it_line;
break;
}
}
}
if (next_line == nullptr) {
// Check whether we closed this loop.
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_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.
assert(first_line->a == last_line->b);
loops.emplace_back(std::move(loop_pts));
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
#endif
} else {
// This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
loop_pts.emplace_back(last_line->b);
open_polylines.emplace_back(OpenPolyline(
IntersectionReference(first_line->a_id, first_line->edge_a_id),
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
}
break;
}
/*
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
*/
assert(last_line->b == next_line->a);
loop_pts.emplace_back(next_line->a);
last_line = next_line;
next_line->set_skip();
}
}
}
使用贪心算法与双索引,将线段连接成闭合环或开放多线段。闭合环数据保存在loops中,开放多线段保存在open_polylines中。
3.2 make_loops 生成闭环
static Polygons make_loops(
// Lines will have their flags modified.
IntersectionLines &lines)
{
Polygons loops;
#if 0
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//#ifdef _DEBUG
for (const Line &l : lines)
assert(l.a != l.b);
#endif /* _DEBUG */
// There should be no tangent edges, as the horizontal triangles are ignored and if two triangles touch at a cutting plane,
// only the bottom triangle is considered to be cutting the plane.
// remove_tangent_edges(lines);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
BoundingBox bbox_svg;
{
static int iRun = 0;
for (const Line &line : lines) {
bbox_svg.merge(line.a);
bbox_svg.merge(line.b);
}
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-raw_lines-%d.svg", iRun ++).c_str(), bbox_svg);
for (const Line &line : lines)
svg.draw(line);
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
std::vector<OpenPolyline> open_polylines;
chain_lines_by_triangle_connectivity(lines, loops, open_polylines);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
svg.draw(union_ex(loops));
for (const OpenPolyline &pl : open_polylines)
svg.draw(Polyline(pl.points), "red");
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Now process the open polylines.
// 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.
chain_open_polylines_exact(open_polylines, loops, false);
chain_open_polylines_exact(open_polylines, loops, true);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
svg.draw(union_ex(loops));
for (const OpenPolyline &pl : open_polylines) {
if (pl.points.empty())
continue;
svg.draw(Polyline(pl.points), "red");
svg.draw(pl.points.front(), "blue");
svg.draw(pl.points.back(), "blue");
}
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Try to close gaps.
// 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.
#if 0
for (double max_gap : { EPSILON, 0.001, 0.1, 1., 2. }) {
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
}
#else
const double max_gap = 2.; //mm
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, false);
chain_open_polylines_close_gaps(open_polylines, loops, max_gap, true);
#endif
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
svg.draw(union_ex(loops));
for (const OpenPolyline &pl : open_polylines) {
if (pl.points.empty())
continue;
svg.draw(Polyline(pl.points), "red");
svg.draw(pl.points.front(), "blue");
svg.draw(pl.points.back(), "blue");
}
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
return loops;
}
chain_lines_by_triangle_connectivity 将线段连接成闭环和多线段
chain_open_polylines_exact 将多线段连接成闭环
chain_open_polylines_close_gaps 将2mm间隙的多线段连接成闭环
3.2 slice_mesh_ex
1395

被折叠的 条评论
为什么被折叠?



