五、OrcaSlicer 切片

一、生成切片层类对象数组

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 &center);
    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 &center) 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 &center);
    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 &center) { 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 &center : out, the center of circle
    // double &diameter: out, the diameter of circle
    bool is_approx_circle(double max_deviation, double max_variance, Point &center, 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 &center);
    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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值