From 49a9b27eab998340216b3ab3720b6df6147ac692 Mon Sep 17 00:00:00 2001 From: cjw Date: Tue, 3 Jun 2025 09:08:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=BC=80=E5=8F=91=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=89=8D=E6=9C=80=E5=90=8E=E6=8F=90=E4=BA=A4=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libslic3r/Fill/Bridge - 副本.cpp | 1276 +++++++++++++++++++++ libslic3r/Fill/Bridge.cpp | 1341 +++++++++++++++++++++++ libslic3r/Fill/Bridge.hpp | 256 +++++ libslic3r/Fill/Fill.cpp | 9 +- libslic3r/Fill/FillBase.cpp | 54 +- libslic3r/Fill/FillBase.hpp | 6 +- libslic3r/Fill/FillConcentric.cpp | 7 + libslic3r/Fill/FillLine.cpp | 18 +- libslic3r/GCode.cpp | 60 +- libslic3r/GCodeWriter.cpp | 10 +- libslic3r/Preset.cpp | 24 +- libslic3r/PresetBundle.cpp | 1 + libslic3r/Print.cpp | 23 +- libslic3r/PrintConfig.cpp | 11 +- libslic3r/PrintConfig.hpp | 2 +- libslic3r/PrintObject.cpp | 34 + libslic3r/PrintObjectSlice.cpp | 79 ++ libslic3r/Slicing.cpp | 2 + libslic3r/TriangleMeshSlicer.cpp | 6 + libslic3r/VariableWidth.cpp | 2 + slic3r/GUI/BackgroundSlicingProcess.cpp | 9 + slic3r/GUI/Plater.cpp | 12 + slic3r/GUI/Tab.cpp | 41 +- slic3r/Utils/CalibUtils.cpp | 16 + slic3r/Utils/CalibUtils.hpp | 1 + 25 files changed, 3246 insertions(+), 54 deletions(-) create mode 100644 libslic3r/Fill/Bridge - 副本.cpp create mode 100644 libslic3r/Fill/Bridge.cpp create mode 100644 libslic3r/Fill/Bridge.hpp diff --git a/libslic3r/Fill/Bridge - 副本.cpp b/libslic3r/Fill/Bridge - 副本.cpp new file mode 100644 index 0000000..1a3b51c --- /dev/null +++ b/libslic3r/Fill/Bridge - 副本.cpp @@ -0,0 +1,1276 @@ +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../Surface.hpp" +#include "../ShortestPath.hpp" +#include "Bridge.hpp" + +//#include +//#include +// +//#include +//#include +//#include +//#include +//#include + +namespace Slic3r { + +// 为 std::pair 定义哈希函数 +//template<> +//struct std::hash> { +// std::size_t operator()(const std::pair& p) const noexcept { +// return std::hash()(p.first) ^ (std::hash()(p.second) << 1); +// } +//}; + +bool is_point_on_segment(const Point_t& p, const Segment& seg, double tolerance = 1e-6) { + // 计算点到线段的距离 + double dist = bg::distance(p, seg); + + // 检查距离是否在容差范围内 + if (dist > tolerance) { + return false; + } + + // 获取线段端点 + const Point_t& p1 = seg.first; + const Point_t& p2 = seg.second; + + // 检查点是否在线段的边界框内 + double min_x = std::min(p1.x(), p2.x()); + double max_x = std::max(p1.x(), p2.x()); + double min_y = std::min(p1.y(), p2.y()); + double max_y = std::max(p1.y(), p2.y()); + + // 考虑容差 + return (p.x() >= min_x - tolerance) && (p.x() <= max_x + tolerance) && + (p.y() >= min_y - tolerance) && (p.y() <= max_y + tolerance); +} + +// 计算点到原点的距离平方 +double distance_to_origin_sq(const Point_t& p) { + return bg::get<0>(p) * bg::get<0>(p) + bg::get<1>(p) * bg::get<1>(p); +} + +// 找到环中距离原点最近的点作为起点 +void reorder_ring_start_point(Ring& ring) { + + // 找到距离原点最近的点 + auto min_it = std::min_element(ring.begin(), ring.end(), + [](const Point_t& a, const Point_t& b) { + return distance_to_origin_sq(a) < distance_to_origin_sq(b); + }); + + // 旋转环使最近的点成为起点 + std::rotate(ring.begin(), min_it, ring.end() - 1); // end()-1 保留闭合点 + + // 确保环保持闭合 + if (!bg::equals(ring.front(), ring.back())) { + ring.back() = ring.front(); + } +} + + + +//将节点加入处理节点集合中 +RingNode Bridge::formatNode(const size_t id, const size_t index, const Ring& ring, const int orientation) { + Ring ringNode; + ringNode.assign(ring.begin(), ring.end()); + RingNode node({ id, index }, ringNode, orientation); + return node; +} +void Bridge::addNode(RingNode& node) { + reorder_ring_start_point(node.ring); + // 检查键是否存在 + const auto it = ringNodes.find(node.id.id); + if (it != ringNodes.end()) { + // 键已存在,将新节点添加到对应向量的末尾 + it->second.emplace_back(node); + } + else { + // 键不存在,创建一个新的向量并插入到 map 中 + ringNodes[node.id.id] = { node }; + } +} + +void Bridge::removeNode(IdIndex id) { + auto it = ringNodes.find(id.id); + if (it != ringNodes.end()) { + std::vector& nodes = it->second; + nodes.erase(std::remove_if(nodes.begin(), nodes.end(), + [id](RingNode x) { return x.id.id == id.id && x.id.index == id.index; }), + nodes.end()); + } +} + +//查找节点 +RingNode& Bridge::findNode(const IdIndex ii) { + const auto it = ringNodes.find(ii.id); + std::vector& nodes = it->second; + const auto it1 = std::find_if(nodes.begin(), nodes.end(), [ii](const RingNode& node) { + return node.id.index == ii.index; + }); + return *it1; +} + + +Polylines Bridge::fill_surface(const Surface* surface, const FillParams& params) +{ + Polylines polylines_out; + + //BoundingBox bounding_box = surface->expolygon.contour.bounding_box(); + Points points = surface->expolygon.contour.points; + Point first = points[0]; + points.push_back(first); + //Polygon outPolygon = bounding_box.polygon(); + Polygon_t polygon; + std::vector outerBoundary; + outerBoundary.reserve(points.size()); + for (Point one : points) { + double x = one.x(); + double y = one.y(); + Point_t temp = { x,y }; + outerBoundary.push_back(temp); + } + bg::append(polygon.outer(), outerBoundary); + + //std::vector innerBoundary; + + //Polygons inPolygons = surface->expolygon.holes; + //for (Polygon one : inPolygons) { + // Ring innerRing; + // innerRing.reserve(one.points.size()); + // for (Point two : one.points) { + // double x = two.x(); + // double y = two.y(); + // Point_t temp = { x,y }; + // innerRing.push_back(temp); + // } + // polygon.inners().push_back(innerRing); + //} + //int inSize = polygon.inners().size(); + // Bridge bridge(polygon, float(scale_(this->overlap - 0.5 * this->spacing))); + this->polygon = polygon; + this->offset = float(scale_(this->overlap - 0.5 * this->spacing)); + printTree(); + //第一个桥接边 + BridgeMap bm = bridges[0]; + IdIndex parent(1, 1); + //遍历 + traverseRing(findNode(bm.from_ii), parent, bm.from, bm.from2, true); + //outputPaths(); + + for (size_t i = 0; i + 1 < path.size(); ++i) { + + Point p1 = { path[i].x(),path[i].y()}; + Point p2 = { path[i + 1].x(),path[i + 1].y()}; + Polyline line = { p1, p2 }; + polylines_out.push_back(line); + } + + return polylines_out; +} + +//void Bridge::_fill_surface_single(const FillParams& params, unsigned int thickness_layers, const std::pair& direction, ExPolygon expolygon, Polylines& polylines_out) +//{ +// expolygon.rotate(-direction.first); +// +// this->_min_spacing = scale_(this->spacing); +// assert(params.density > 0.0001f && params.density <= 1.f); +// this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density); +// this->_diagonal_distance = this->_line_spacing * 2; +// this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill +// BoundingBox bounding_box = expolygon.contour.bounding_box(); +// +// // define flow spacing according to requested density +// if (params.density > 0.9999f && !params.dont_adjust) { +// this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); +// this->spacing = unscale(this->_line_spacing); +// } +// else { +// // extend bounding box so that our pattern will be aligned with other layers +// // Transform the reference point to the rotated coordinate system. +// bounding_box.merge(align_to_grid( +// bounding_box.min, +// Point(this->_line_spacing, this->_line_spacing), +// direction.second.rotated(-direction.first))); +// } +// Polygon outPolygon = bounding_box.polygon(); +// Polygon_t polygon; +// std::vector outerBoundary; +// outerBoundary.reserve(outPolygon.points.size()); +// for (Point one : outPolygon.points) { +// double x = one.x(); +// double y = one.y(); +// Point_t temp = { x,y }; +// outerBoundary.push_back(temp); +// } +// bg::append(polygon.outer(), outerBoundary); +// +// //std::vector innerBoundary; +// +// Polygons inPolygons = expolygon.holes; +// for (Polygon one :inPolygons) { +// Ring innerRing; +// for (Point two: one.points) { +// double x = two.x(); +// double y = two.y(); +// Point_t temp = { x,y }; +// innerRing.push_back(temp); +// } +// polygon.inners().push_back(innerRing); +// } +// this->polygon = polygon; +// this->offset = float(scale_(this->overlap - 0.5 * this->spacing)); +// printTree(); +// for (size_t i = 0; i + 1 < path.size(); ++i) { +// +// Point p1 = { path[i].x(),path[i].y()}; +// Point p2 = { path[i + 1].x(),path[i + 1].y()}; +// Polyline line = { p1, p2 }; +// polylines_out.push_back(line); +// } +//} + +/*void Bridge::_fill_surface_single(const FillParams& params, unsigned int thickness_layers, const std::pair& direction, ExPolygon expolygon, Polylines& polylines_out) +{ + + + //this->_min_spacing = scale_(this->spacing); + //assert(params.density > 0.0001f && params.density <= 1.f); + //this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density); + //this->_diagonal_distance = this->_line_spacing * 2; + //this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill + //BoundingBox bounding_box = expolygon.contour.bounding_box(); + + //// define flow spacing according to requested density + ////根据要求的密度定义流间距 + //if (params.density > 0.9999f && !params.dont_adjust) { + // this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); + // this->spacing = unscale(this->_line_spacing); + //} + //else { + // // extend bounding box so that our pattern will be aligned with other layers + // // Transform the reference point to the rotated coordinate system. + // //扩展边界框,使我们的图案与其他层对齐 + // //将参考点变换到旋转坐标系。 + // bounding_box.merge(align_to_grid( + // bounding_box.min, + // Point(this->_line_spacing, this->_line_spacing), + // direction.second.rotated(-direction.first))); + //} + + BoundingBox bounding_box = expolygon.contour.bounding_box(); + Polygon outPolygon = bounding_box.polygon(); + Polygon_t polygon; + std::vector outerBoundary; + outerBoundary.reserve(outPolygon.points.size()); + for (Point one : outPolygon.points) { + double x = one.x(); + double y = one.y(); + Point_t temp = { x,y }; + outerBoundary.push_back(temp); + } + bg::append(polygon.outer(), outerBoundary); + + //std::vector innerBoundary; + + Polygons inPolygons = expolygon.holes; + for (Polygon one : inPolygons) { + Ring innerRing; + innerRing.reserve(one.points.size()); + for (Point two : one.points) { + double x = two.x(); + double y = two.y(); + Point_t temp = { x,y }; + innerRing.push_back(temp); + } + polygon.inners().push_back(innerRing); + } + int inSize = polygon.inners().size(); + // Bridge bridge(polygon, float(scale_(this->overlap - 0.5 * this->spacing))); + this->polygon = polygon; + this->offset = float(scale_(this->overlap - 0.5 * this->spacing)); + printTree(); + //outputPaths(); + + for (size_t i = 0; i + 1 < path.size(); ++i) { + + Point p1 = { path[i].x(),path[i].y()}; + Point p2 = { path[i + 1].x(),path[i + 1].y()}; + Polyline line = { p1, p2 }; + polylines_out.push_back(line); + } +}*/ + +//计算多边形的外边界和内边界 +void Bridge::computePolygonBoundaries(const Polygon_t& polygon) { + // 提取外边界 + boundaries.outerBoundary = bg::exterior_ring(polygon); + // 获取内边界 + const auto& interiors = bg::interior_rings(polygon); + for (const auto & inner : interiors) { + boundaries.innerBoundaries.push_back(inner); + } +} + +// 判断两个环是否相交(不包括包含关系) +bool Bridge::ringsIntersect(const Ring& ring1, const Ring& ring2) +{ + // 首先检查是否相交(包括边界相交) + if (!bg::intersects(ring1, ring2)) { + return false; + } + + // 然后排除包含关系 + bool ring1_inside_ring2 = bg::within(ring1, ring2); + bool ring2_inside_ring1 = bg::within(ring2, ring1); + + // 如果存在包含关系,则不算作相交 + if (ring1_inside_ring2 || ring2_inside_ring1) { + return false; + } + + return true; +} + + +//获取环偏移后的环(可能为空) +std::vector Bridge::offsetRing(const Ring& ring, const double distance) { + // 将环包装成多边形 + Polygon_t inputPolygon; + inputPolygon.outer() = ring; + // 修复几何数据 + bg::correct(inputPolygon); + + // 使用 multi_polygon 作为 buffer 的输出类型 + bg::model::multi_polygon result; + // 使用对称距离策略 + bg::buffer(inputPolygon, result, + bg::strategy::buffer::distance_symmetric(distance), + bg::strategy::buffer::side_straight(), + bg::strategy::buffer::join_round(), + bg::strategy::buffer::end_round(), + bg::strategy::buffer::point_circle()); + // 提取偏移后的环 + std::vector offsetRing; + for (const auto& poly : result) { + offsetRing.emplace_back(poly.outer()); + } + return offsetRing; +} + +// 查找环非邻边的相交点 +std::vector Bridge::findNonAdjacentIntersections(const Ring& ring) { + Polygon_t poly; + bg::append(poly.outer(), ring); + std::vector intersections; + std::vector> segments; + const size_t num_points = bg::num_points(poly.outer()); + + // 构建所有边的线段 + for (int i = 0; i < num_points - 1; ++i) { + segments.emplace_back(poly.outer()[i], poly.outer()[i + 1]); + } + + // 遍历所有非邻边对 + for (size_t i = 0; i < segments.size(); ++i) { + for (size_t j = i + 2; j < segments.size(); ++j) { + if ((i == 0 && j == segments.size() - 1)) continue; // 排除相邻边 + std::vector intersection_points; + bg::intersection(segments[i], segments[j], intersection_points); + + for (const auto& point : intersection_points) { + // 检查相交点是否为多边形的顶点 + bool is_vertex = false; + for (const auto& vertex : polygon.outer()) { + if (bg::equals(point, vertex)) { + is_vertex = true; + break; + } + } + if (!is_vertex) { + intersections.push_back(point); + } + } + } + } + return intersections; +} + +// 将环拆分成多个环 +std::vector Bridge::split(const Ring& ring, const std::vector& intersections) { + std::vector rings; + std::vector visited(ring.size(), false); + Ring extendedRing = ring; + + // 插入相交点到环中 + for (const auto& intersection : intersections) { + for (size_t i = 0; i < ring.size(); ++i) { + Segment seg(ring[i], ring[(i + 1) % ring.size()]); + if (bg::within(intersection, seg)) { + extendedRing.insert(extendedRing.begin() + i + 1, intersection); + break; + } + } + } + + std::vector newVisited(extendedRing.size(), false); + for (size_t i = 0; i < extendedRing.size(); ++i) { + if (newVisited[i]) continue; + + Ring newRing; + size_t current = i; + do { + newRing.push_back(extendedRing[current]); + newVisited[current] = true; + current = (current + 1) % extendedRing.size(); + } while (current != i); + + if (newRing.size() > 2) { + rings.push_back(newRing); + } + } + return rings; +} + + +// 在环的指定位置插入点 +void insert_point_in_ring(Ring& ring, const Point_t& point, size_t after_index) { + auto it = ring.begin(); + std::advance(it, after_index + 1); + ring.insert(it, point); +} + +// 找到环上应该插入交点的位置 +std::vector> find_insertion_points(const Ring& ring, const std::vector& intersections) { + std::vector> insertion_points; + + for (const auto& p : intersections) { + double min_dist = std::numeric_limits::max(); + size_t best_segment = 0; + + for (size_t i = 0; i < ring.size() - 1; ++i) { + Point_t seg_start = ring[i]; + Point_t seg_end = ring[i + 1]; + + // 计算点到线段的距离 + double dist = bg::distance(p, bg::model::segment(seg_start, seg_end)); + + if (dist < min_dist) { + min_dist = dist; + best_segment = i; + } + } + + if (min_dist < 1e-6) { // 使用小的容差值 + insertion_points.emplace_back(best_segment, p); + } + } + + // 按插入位置排序,从后往前插入 + std::sort(insertion_points.begin(), insertion_points.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + + return insertion_points; +} + +// 合并两个环 +std::vector Bridge::merge(const RingNode& ring1, const RingNode& ring2) { + + // 将环转换为多边形 + Polygon_t poly1, poly2; + bg::append(poly1.outer(), ring1.ring); + bg::append(poly2.outer(), ring2.ring); + + bg::correct(poly1); // 确保多边形是有效的 + bg::correct(poly2); // 确保多边形是有效的 + + std::vector intersection_points; + bg::intersection(poly1, poly2, intersection_points); + + // 将交点插入到两个多边形中 + auto insertions1 = find_insertion_points(poly1.outer(), intersection_points); + for (const auto& ins : insertions1) { + insert_point_in_ring(poly1.outer(), ins.second, ins.first); + } + + auto insertions2 = find_insertion_points(poly2.outer(), intersection_points); + for (const auto& ins : insertions2) { + insert_point_in_ring(poly2.outer(), ins.second, ins.first); + } + + + // 计算多边形交、差布尔运算 + /* MultiPolygon mp0; + bg::intersection(poly1, poly2, mp0);*/ + + MultiPolygon mp1; + bg::difference(poly1, poly2, mp1); + + MultiPolygon mp2; + bg::difference(poly2,poly1, mp2); + + // 合并所有结果多边形 + MultiPolygon result; + //result.insert(result.end(), mp0.begin(), mp0.end()); + if (ring1.orientation == -1 && ring2.orientation == 1) { + result.insert(result.end(), mp1.begin(), mp1.end()); + } + else if(ring1.orientation == 1 && ring2.orientation == -1){ + result.insert(result.end(), mp2.begin(), mp2.end()); + } + else { + result.insert(result.end(), mp1.begin(), mp1.end()); + result.insert(result.end(), mp2.begin(), mp2.end()); + } + + std::vector allRings; + + for (const auto& poly : result) { + allRings.emplace_back(poly.outer()); + } + + //for (const auto& poly : result) { + // allRings.push_back(poly.outer()); + // for (const auto& inner : poly.inners()) { + // allRings.push_back(inner); + // } + //} + + //// 过滤掉被其他环完全包含的环 + std::vector disjointRings; + for (size_t i = 0; i < allRings.size(); ++i) { + bool isContained = false; + for (size_t j = 0; j < allRings.size(); ++j) { + if (i != j && bg::within(allRings[i], allRings[j])) { + isContained = true; + break; + } + } + if (!isContained) { + disjointRings.push_back(allRings[i]); + } + } + return allRings; +} + +//生成环集 +void Bridge::generateRings() { + //获取多边形的外边界和内边界 + computePolygonBoundaries(polygon); + + //外边界 + std::vector rings = offsetRing(boundaries.outerBoundary, -offset / 2); + for (auto& r : rings) { + RingNode node = formatNode(maxRid++, 1, r, -1); + nodeHandles.emplace_back(node); + addNode(node); + } + + //内边界 偏移 + std::map offRings; //存放待偏移的环 + for (auto& inner : boundaries.innerBoundaries) { + offRings.insert({{maxRid++,1},inner }); + } + std::vector tempRings; + for (auto& r : offRings) { + std::vector _rings = offsetRing(r.second, offset / 2); + if (_rings.size() == 1) { + RingNode node1 = formatNode(r.first.id, r.first.index + 1, _rings[0], 1); + if (bg::area(node1.ring) > area_threshold) { + addNode(node1); + tempRings.emplace_back(node1); + } + } + else if (_rings.size() > 1) { + std::vector id_indices; + for (auto& r1 : _rings) { + RingNode node0 = formatNode(++maxRid, 1, r1, 1); + if (bg::area(node0.ring) > area_threshold) { + addNode(node0); + tempRings.emplace_back(node0); + IdIndex ii(maxRid, 1); + id_indices.emplace_back(ii); + } + } + //分裂映射 + offsetMap.insert(std::pair>({ r.first.id,r.first.index }, id_indices)); + } + } + nodeHandles.insert(nodeHandles.end(), tempRings.begin(), tempRings.end()); + + //遍历当前用于判断自/互交的节点集 + while (!nodeHandles.empty()) { + std::vector interNodes; //产生互交的节点集 + std::vector nodes; //互交后产生的节点 + for (auto it1 = nodeHandles.begin(); it1 != nodeHandles.end(); ++it1) { + //互交判断&处理 + auto it2 = std::next(it1); + for (;it2 != nodeHandles.end(); ++it2) { + if (ringsIntersect(it1->ring, it2->ring)) { + //合并 + std::vector rings = merge(*it1, *it2); + std::vector id_indices; + for (const auto& ring2 : rings) { + // 外与内 外与外 -> 向内; 内与内 -> 向外 + const int orientation = (it1->orientation + it2->orientation) <= 0 ? -1 : 1; + RingNode node1 = formatNode(++maxRid, 1, ring2, orientation); + if (bg::area(node1.ring) > area_threshold) { + nodes.emplace_back(node1); + addNode(node1); + IdIndex _ii(maxRid, 1); + id_indices.emplace_back(_ii); + } + } + MergeMap mm(it1->id, it2->id, id_indices); + mergeMap.emplace_back(mm); + interNodes.emplace_back(*it1); + interNodes.emplace_back(*it2); + it1++; + break; + } + } + } + //删除互交节点 + for (auto& n : interNodes) { + nodeHandles.erase(std::remove(nodeHandles.begin(), nodeHandles.end(), n), nodeHandles.end()); + } + + //是否存在互交 + if (!nodes.empty()) { + for (auto& n : nodes) { + nodeHandles.emplace_back(n); + } + } + std::vector offsetNodes; + //偏移 + for (auto& node : nodeHandles) { + //if (node.orientation == 1) continue; + std::vector ring0 = offsetRing(node.ring, static_cast(node.orientation) * offset); + if (ring0.size() == 1) { + RingNode node0 = formatNode(node.id.id, ++node.id.index, ring0[0], node.orientation); + if (bg::area(node0.ring) > area_threshold) { + addNode(node0); + offsetNodes.emplace_back(node0); + } + } + else if (ring0.size() > 1) { + std::vector id_indices; + for (auto& r : ring0) { + RingNode node0 = formatNode(++maxRid, 1, r, node.orientation); + if (bg::area(node0.ring) > area_threshold) { + addNode(node0); + offsetNodes.emplace_back(node0); + IdIndex ii(maxRid, 1); + id_indices.emplace_back(ii); + } + } + //分裂映射 + offsetMap.insert(std::pair>({node.id.id,node.id.index }, id_indices)); + } + } + //清空 + nodeHandles.clear(); + if (!offsetNodes.empty()) { + //重填 + nodeHandles.insert(nodeHandles.end(), offsetNodes.begin(), offsetNodes.end()); + } + } +} + +//形成树 +void Bridge::formatTree() { + //遍历不同环类型的节点集合 + for (auto& ringNode : ringNodes) { + // 使用反向迭代器进行反序遍历 + std::vector& vec = ringNode.second; + const auto it0 = ringNode.second.begin(); + if (it0->orientation == 1) { // 向外 倒序 + for (int i = ringNode.second.size() - 1; i > 0; i--) { + ringNode.second[i].children.emplace_back(ringNode.second[i - 1]); + ringNode.second[i - 1].parent = &ringNode.second[i]; + } + } + else { //向内 正序 + for (int i = 0; i < vec.size() - 1; i++) { + ringNode.second[i].children.emplace_back(ringNode.second[i + 1]); + ringNode.second[i + 1].parent = &ringNode.second[i]; + } + } + } + + //遍历分裂/合并映射 + for (auto& map : offsetMap) { //偏移产生的分裂 + RingNode& it1 = findNode(map.first); + for (const auto& ii : map.second) { + RingNode& it2 = findNode(ii); + it1.children.emplace_back(it2); + } + } + for (auto& map : mergeMap) { //合并 + RingNode& it1 = findNode(map.ii1); + RingNode& it2 = findNode(map.ii2); + for (const auto& ii : map.nodes) { + RingNode& it = findNode(ii); + + if (it.orientation == -1) { //向内 + //内 外 + if (it1.orientation == -1 && it2.orientation == 1) { + it1.parent->children.emplace_back(it); + it.parent = it1.parent; + IdIndex _ii = it1.id; + + //将it1从父节点的子节点集中移除 + it1.parent->children.erase(std::remove_if(it1.parent->children.begin(), it1.parent->children.end(), + [_ii](RingNode x) { return x.id.id == _ii.id && x.id.index == _ii.index; }), + it1.parent->children.end()); + //it作为it2子节点的父节点 + for (auto& c : it2.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it2.children.begin(), it2.children.end()); + } + else if (it1.orientation == 1 && it2.orientation == -1) //外 内 + { + it2.parent->children.emplace_back(it); + it.parent = it2.parent; + IdIndex _ii = it2.id; + //将it2从父节点的子节点集中移除 + it2.parent->children.erase(std::remove_if(it2.parent->children.begin(), it2.parent->children.end(), + [_ii](RingNode x) { return x.id.id == _ii.id && x.id.index == _ii.index; }), + it2.parent->children.end()); + //it作为it1子节点的父节点 + for (auto& c : it1.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it1.children.begin(), it1.children.end()); + } + else if (it1.orientation == 1 && it2.orientation == 1) { //外 外 + //it作为it1子节点的父节点 + for (auto& c : it1.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it1.children.begin(), it1.children.end()); + //it作为it2子节点的父节点 + for (auto& c : it2.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it2.children.begin(), it2.children.end()); + } + else if (it1.orientation == -1 && it2.orientation == -1) {// 内 内 + it1.parent->children.emplace_back(it); + //it.parent = it1.parent; + IdIndex _ii = it1.id; + //将it1从父节点的子节点集中移除 + it1.parent->children.erase(std::remove_if(it1.parent->children.begin(), it1.parent->children.end(), + [_ii](RingNode x) { return x.id.id == _ii.id && x.id.index == _ii.index; }), + it1.parent->children.end()); + + it2.parent->children.emplace_back(it); + //it.parent = it2.parent; + IdIndex _ii2 = it2.id; + //将it2从父节点的子节点集中移除 + it2.parent->children.erase(std::remove_if(it2.parent->children.begin(), it2.parent->children.end(), + [_ii2](RingNode x) { return x.id.id == _ii2.id && x.id.index == _ii2.index; }), + it2.parent->children.end()); + } + } + else { //向外 + //it1.parent = ⁢ + //将it1的子节点作为it的子节点 + it.children.insert(it.children.end(), it1.children.begin(), it1.children.end()); + it1.children.clear(); //清空it1的子节点集 + //it2.parent = ⁢ + it.children.insert(it.children.end(), it2.children.begin(), it2.children.end()); + it2.children.clear(); + } + + RingNode* it_parent = it.parent; + //判断是否互交 + if (ringsIntersect(it.ring, it_parent->ring)) { + //合并 + std::vector rings = merge(it1, *it1.parent); + const int orientation = (it1.orientation + it1.parent->orientation) <= 0 ? -1 : 1; + RingNode node1 = formatNode(++maxRid, 1, rings[0], orientation); + if (bg::area(node1.ring) > area_threshold) { + addNode(node1); + + findNode(node1.id).children.insert(findNode(node1.id).children.end(), it1.children.begin(), it1.children.end()); + if (it_parent->parent != nullptr) { + IdIndex ii = it_parent->id; + it_parent->parent->children.erase(std::remove_if(it_parent->parent->children.begin(), it_parent->parent->children.end(), + [ii](RingNode x) { return x.id.id == ii.id && x.id.index == ii.index; }), + it_parent->parent->children.end()); + it_parent->parent->children.emplace_back(node1); + } + } + } + } + } +} + +//树遍历 +void Bridge::dfs(RingNode& node, std::vector& visited, size_t& edge) { + auto& it = std::find_if(visited.begin(), visited.end(), [node](const IdIndex& ii) { + return ii.id == node.id.id && ii.index == node.id.index; + }); + if (it != visited.end()) { + return; + } + visited.emplace_back(node.id); + edge++; + // 处理当前节点 + std::cout << "rId: " << node.id.id << ", rIndex: " << node.id.index + << ", orientation: " << node.orientation + << ", isHide: " << (node.isHide ? "true" : "false") << std::endl; + + Polygon_t poly; + bg::assign(poly, node.ring); + //verteies.push_back(convertToSFML(poly, sf::Color::Red)); + + // 递归遍历子节点 + for (auto& child : node.children) { + handleBridge(node.id, child.id, child.ring.size(),edge); + dfs(findNode(child.id), visited, edge); + } +} + + +//找到多边形环第 N 条边的中心点 +Point_t Bridge::findCenterPointOnEdge(Ring& ring, size_t N) { + + // 获取第 N 条边的两个端点 + const Point_t start = ring[N]; + const Point_t end = ring[N + 1]; + // 计算中点 + const double mid_x = (start.x() + end.x()) / 2; + const double mid_y = (start.y() + end.y()) / 2; + + Point_t p(mid_x, mid_y); + const auto insertPos = ring.begin() + N + 1; + ring.insert(insertPos, p); + + bg::correct(ring); //调整方向为顺时针 + return p; +} + +// 计算点到线段的最近点 +Point_t closest_point_on_segment(const Point_t& p, const Point_t& seg_start, const Point_t& seg_end) { + const double x = bg::get<0>(p); + const double y = bg::get<1>(p); + const double x1 = bg::get<0>(seg_start); + const double y1 = bg::get<1>(seg_start); + const double x2 = bg::get<0>(seg_end); + const double y2 = bg::get<1>(seg_end); + + // 线段向量 + const double dx = x2 - x1; + const double dy = y2 - y1; + + // 如果线段长度为零,返回起点 + if (dx == 0 && dy == 0) { + return seg_start; + } + + // 计算投影参数t + const double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); + + // 根据t值确定最近点 + if (t <= 0.0) { + return seg_start; + } + else if (t >= 1.0) { + return seg_end; + } + else { + return Point_t(x1 + t * dx, y1 + t * dy); + } +} + +// 在环的边上查找离给定点最近的点 +Point_t find_closest_point_on_ring_edges(Ring& ring, const Point_t& p0) { + // 验证环是否闭合 + if (ring.size() < 2 || !bg::equals(ring.front(), ring.back())) { + throw std::runtime_error("Ring is not closed"); + } + + Point_t closest_point; + double min_distance = std::numeric_limits::max(); + + size_t e_index = 0; + // 遍历环的所有边 + for (size_t i = 0; i < ring.size() - 1; ++i) { + const Point_t& p1 = ring[i]; + const Point_t& p2 = ring[i + 1]; + + // 计算线段上的最近点 + Point_t projected_point = closest_point_on_segment(p0, p1, p2); + + // 计算距离 + double distance = bg::distance(p0, projected_point); + + // 更新最近点 + if (distance < min_distance) { + min_distance = distance; + closest_point = projected_point; + e_index = i; + } + } + //插入 + ring.insert(ring.begin()+e_index+1, closest_point); + + bg::correct(ring); //调整方向为顺时针 + return closest_point; +} + +// 找到点所在的边(返回边起点索引) +std::pair find_edge_containing_point(const Ring& ring, const Point_t& point) { + for (size_t i = 0; i < ring.size() - 1; ++i) { + const Point_t& p1 = ring[i]; + const Point_t& p2 = ring[i + 1]; + + // 计算点到线段的距离 + double seg_length = bg::distance(p1, p2); + if (seg_length == 0) continue; // 忽略零长度边 + + // 计算点在边上的投影参数t + double t = ((bg::get<0>(point) - bg::get<0>(p1)) * (bg::get<0>(p2) - bg::get<0>(p1)) + + (bg::get<1>(point) - bg::get<1>(p1)) * (bg::get<1>(p2) - bg::get<1>(p1))) / + (seg_length * seg_length); + + // 如果点在当前边上 + if (t >= 0.0 && t <= 1.0) { + Point_t proj( + bg::get<0>(p1) + t * (bg::get<0>(p2) - bg::get<0>(p1)), + bg::get<1>(p1) + t * (bg::get<1>(p2) - bg::get<1>(p1)) + ); + + // 检查投影点与实际点的距离是否足够近 + if (bg::distance(point, proj) < 1e-6) { + return { i, t }; + } + } + } +} + + +Point_t Bridge::find_point_at_distance_clockwise(Ring& ring, const Point_t& start_point, double d) +{ + // 1. 找到起点所在的边 + std::pair p = find_edge_containing_point(ring, start_point); + size_t edge_index = p.first; + double t = p.second; + // 计算从起点到当前边终点的剩余距离 + const Point_t& p1 = ring[edge_index]; + const Point_t& p2 = ring[(edge_index + 1) % ring.size() ]; + double remaining_on_edge = (1.0 - t) * bg::distance(p1, p2); + + // 3. 如果剩余距离足够 + if (remaining_on_edge >= d) { + double new_t = t + d / bg::distance(p1, p2); + Point_t p(bg::get<0>(p1) + new_t * (bg::get<0>(p2) - bg::get<0>(p1)), + bg::get<1>(p1) + new_t * (bg::get<1>(p2) - bg::get<1>(p1))); + auto insertPos = ring.begin() + edge_index + 1; + ring.insert(insertPos, p); + bg::correct(ring); //调整方向为顺时针 + return p; + } + + // 4. 否则继续遍历后续边 + double accumulated_distance = remaining_on_edge; + size_t current_index = (edge_index + 1) % (ring.size() - 1); + + while (accumulated_distance < d) { + const Point_t& p1 = ring[current_index]; + const Point_t& p2 = ring[(current_index + 1) % (ring.size() - 1)]; + double segment_length = bg::distance(p1, p2); + + if (accumulated_distance + segment_length >= d) { + double ratio = (d - accumulated_distance) / segment_length; + //在current_index 插入 + Point_t p(bg::get<0>(p1) + ratio * (bg::get<0>(p2) - bg::get<0>(p1)), + bg::get<1>(p1) + ratio * (bg::get<1>(p2) - bg::get<1>(p1))); + auto insertPos = ring.begin() + current_index + 1; + ring.insert(insertPos, p); + bg::correct(ring); //调整方向为顺时针 + return p; + } + accumulated_distance += segment_length; + current_index = (current_index + 1) % (ring.size() - 1); + } + return Point_t(); +} + + +// 判断点是否在线段内部(非端点) +bool is_point_inside_segment(const Point_t& p, const Segment& seg) { + double x = bg::get<0>(p); + double y = bg::get<1>(p); + double x1 = bg::get<0>(seg.first); + double y1 = bg::get<1>(seg.first); + double x2 = bg::get<0>(seg.second); + double y2 = bg::get<1>(seg.second); + + // 检查点是否在线段的包围盒内 + if ((x < std::min(x1, x2) - 1e-6) || (x > std::max(x1, x2) + 1e-6) || + (y < std::min(y1, y2) - 1e-6) || (y > std::max(y1, y2) + 1e-6)) { + return false; + } + + // 向量叉积判断共线(可跳过,因为 intersects 已保证相交) + double cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1); + if (std::abs(cross) > 1e-6) return false; + + return true; +} + +// 判断两线段是否在内部相交(排除端点接触) +bool segments_cross(const Segment& seg1, const Segment& seg2) { + if (!bg::intersects(seg1, seg2)) return false; + + std::vector points; + bg::intersection(seg1, seg2, points); + + if (points.empty()) return false; + + // 检查交点是否在两条线段的内部 + for (const auto& p : points) { + if (is_point_inside_segment(p, seg1) && is_point_inside_segment(p, seg2)) { + return true; + } + } + return false; +} + +//在环上寻找长度最大的边索引 +size_t Bridge::findLongestEdgeIndex(const Ring& ring) { + if (ring.size() < 4) { + throw std::invalid_argument("Ring must have at least 4 points"); + } + + std::vector max_indices; + double max_length = 0.0; + const size_t n = ring.size(); + + // 遍历所有边,收集最大长度边的索引 + for (size_t i = 0; i < n - 1; ++i) { + const auto& p1 = ring[i]; + const auto& p2 = ring[i + 1]; + const double current_length = bg::distance(p1, p2); + + if (current_length > max_length) { + max_length = current_length; + max_indices.clear(); + max_indices.push_back(i); + } + else if (current_length == max_length) { + max_indices.push_back(i); + } + } + isFront = !isFront; + if (max_indices.size() > 1) { + sel_edge_index = isFront ? max_indices[0] : max_indices[max_indices.size() - 1]; + } + else { + //std::random_device rd; + //std::mt19937 gen(rd()); + //std::uniform_int_distribution<> dis(1, n); + + sel_edge_index = isFront?(max_indices[0] + 1) % (n - 1):(max_indices[0]-1+n)%(n-1); + } + return sel_edge_index; + +} + +//判断两个点是否相等 +bool equal(Point_t p1, Point_t p2) { + return p1.x() == p2.x() && p1.y() == p2.y(); +} + +//在第N条边上进行内外环桥接,并插入桥接点 +void Bridge::handleBridge(IdIndex o_ii, IdIndex i_ii,const size_t ring_size, const size_t N) +{ + //随机选择桥接的边 + RingNode& inner = findNode(i_ii); + RingNode& outer = findNode(o_ii); + //从内到外 + size_t edge_index = findLongestEdgeIndex(inner.ring); + Point_t p0 = findCenterPointOnEdge(inner.ring, edge_index); + Point_t p1 = find_point_at_distance_clockwise(inner.ring, p0, offset); + Point_t p2 = find_closest_point_on_ring_edges(outer.ring,p0); + Point_t p3 = find_point_at_distance_clockwise(outer.ring, p2, offset); + + //从外到内 + edge_index = findLongestEdgeIndex(outer.ring); + Point_t p2_1 = findCenterPointOnEdge(outer.ring, edge_index); + Point_t p3_1 = find_point_at_distance_clockwise(outer.ring, p2_1, offset); + Point_t p0_1 = find_closest_point_on_ring_edges(inner.ring, p2_1); + Point_t p1_1 = find_point_at_distance_clockwise(inner.ring, p0_1, offset); + + + //判断环上是否存在桥接 + bool isExist = false; + for (auto& b : bridges) { + if (b.to_ii == i_ii) { + isExist = true; + break; + } + } + if (isExist) return; + + if (bg::distance(p1, p2) < bg::distance(p1_1, p2_1)) { + Segment seg1(p2, p1); + Segment seg2(p3, p0); + if (bg::intersects(seg1, seg2)) { + BridgeMap edge(p2, p1, p3, p0, o_ii, i_ii); + bridges.push_back(edge); + } + else { + BridgeMap edge(p2, p0, p3, p1, o_ii, i_ii); + bridges.push_back(edge); + } + } + else { + Segment seg1(p2_1, p1_1); + Segment seg2(p3_1, p0_1); + if (bg::intersects(seg1, seg2)) { + BridgeMap edge(p2_1, p1_1, p3_1, p0_1, o_ii, i_ii); + bridges.push_back(edge); + } + else { + BridgeMap edge(p2_1, p0_1, p3_1, p1_1, o_ii, i_ii); + bridges.push_back(edge); + } + } + + +} + + +//输出所有环 +void Bridge::printRings() { + generateRings(); + // 输出环中的点信息 + std::cout << "Points in the ring:" << std::endl; + for (const auto& map : ringNodes) { + std::cout << "(" << map.first << std::endl; + for (const auto& node : map.second) { + std::cout << "{" << node.id.id << "," << node.id.index << "," << node.orientation << std::endl; + for (const auto& point : node.ring) { + std::cout << "(" << bg::get<0>(point) << "," << bg::get<1>(point) << ")"; + } + std::cout << "}" << std::endl; + } + std::cout << ")" << std::endl; + } +} + +//输出树结构 +void Bridge::printTree() { + generateRings(); + formatTree(); + // 用于记录已访问的节点 + std::vector visited; + size_t edge = 1; + dfs(findNode({ 1,1 }), visited,edge); +} + +std::vector Bridge::outputPaths() +{ + return path; +} + + +//点在环上的位置索引 +int findPointIndex(const Ring& ring, const Point_t& p0) { + for (size_t i = 0; i < ring.size(); ++i) { + if (ring[i].x() == p0.x() && ring[i].y() == p0.y()) { + return static_cast(i); + } + } + return -1; // 未找到 +} + +int findIndex(std::vector points, const Point_t& p0) { + for (int i = 0; i < points.size(); ++i) { + if (equal(points[i], p0)){ + return static_cast(i); + } + } + return -1; // 未找到 +} + + + +//递归遍历环 +void Bridge::traverseRing( + RingNode& node, + IdIndex parent, + Point_t& start, + Point_t& end, + bool isOutermostLayer +) { + //得到start点在环ring上的索引位置 + int size = node.ring.size(); + int s_index = findPointIndex(node.ring, start); + int e_index = findPointIndex(node.ring, end); + std::vector c_points; + std::vector cc_points; + std::vector points; + for (int i = s_index; !equal(node.ring[i], end); i = (i + 1) % size) { + c_points.emplace_back(node.ring[i]); + } + c_points.emplace_back(end); + for (int i = s_index; !equal(node.ring[i], end); i = (i - 1 + size) % size) { + cc_points.emplace_back(node.ring[i]); + } + cc_points.emplace_back(end); + + if (cc_points.size() > c_points.size()) { + points.assign(cc_points.begin(), cc_points.end()); + } + else + { + points.assign(c_points.begin(), c_points.end()); + } + int index = 0; + do { + //转换成SFML 顶点 + path.emplace_back(points[index]); + + BridgeMap* bridge = nullptr; + //判断该点是否为桥接点 + for (auto& bm : bridges) { + if (equal(bm.from, points[index]) || equal(bm.from2, points[index])) { + bridge = &bm; + break; + } + } + if (bridge != nullptr) { + RingNode& rn = findNode(bridge->to_ii); //to 环 + if (equal(bridge->from, points[index])) { + traverseRing(rn, node.id, bridge->to, bridge->to2, false); + index = findIndex(points, bridge->from2); + } + else if (equal(bridge->from2, points[index])) { + traverseRing(rn, node.id, bridge->to2, bridge->to, false); + index = findIndex(points, bridge->from); + } + if (index == -1) + break; + /* path.emplace_back(sf::Vertex( + sf::Vector2f(static_cast(points[index].x()), + static_cast(points[index].y())), + sf::Color::Red + ));*/ + path.emplace_back(points[index]); + } + index += isOutermostLayer ? -1 : 1; + } while (index > 0 && index < points.size()); + + +} +} // namespace Slic3r + + diff --git a/libslic3r/Fill/Bridge.cpp b/libslic3r/Fill/Bridge.cpp new file mode 100644 index 0000000..e285277 --- /dev/null +++ b/libslic3r/Fill/Bridge.cpp @@ -0,0 +1,1341 @@ +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../Surface.hpp" +#include "../ShortestPath.hpp" + +#include "Bridge.hpp" + +//#include +//#include +// +//#include +//#include +//#include +//#include +//#include +//namespace mpd = boost::multiprecision::decimal; +namespace Slic3r { +// 为 std::pair 定义哈希函数 +//template<> +//struct std::hash> { +// std::size_t operator()(const std::pair& p) const noexcept { +// return std::hash()(p.first) ^ (std::hash()(p.second) << 1); +// } +//}; + Polylines Bridge::fill_surface(const Surface* surface, const FillParams& params) + { + Polylines polylines_out; + + //BoundingBox bounding_box = surface->expolygon.contour.bounding_box(); + Points points = surface->expolygon.contour.points; + Point first = points[0]; + points.push_back(first); + //Polygon outPolygon = bounding_box.polygon(); + Polygon_t polygon; + std::vector outerBoundary; + outerBoundary.reserve(points.size()); + int precision = 1e7; + for (Point one : points) { + double x = one.x(); + double y = one.y(); + //double x_result = x / 1000000.0 + 200; + //double y_result = y / 1000000.0 + 200; + //double stored_x = std::round(x_result * precision) / precision; + //double stored_y = std::round(y_result * precision) / precision; + //Point_t temp = { stored_x ,stored_y }; + Point_t temp = { x ,y }; + outerBoundary.push_back(temp); + } + bg::append(polygon.outer(), outerBoundary); + + //std::vector innerBoundary; + + //Polygons inPolygons = surface->expolygon.holes; + //for (Polygon one : inPolygons) { + // Ring innerRing; + // innerRing.reserve(one.points.size()); + // for (Point two : one.points) { + // double x = two.x(); + // double y = two.y(); + // Point_t temp = { x,y }; + // innerRing.push_back(temp); + // } + // polygon.inners().push_back(innerRing); + //} + //int inSize = polygon.inners().size(); + // Bridge bridge(polygon, float(scale_(this->overlap - 0.5 * this->spacing))); + this->polygon = polygon; + auto abc = float(scale_(this->overlap - 0.5 * this->spacing)); + if (abc < 0) { + abc = -abc; + } + //this->offset = abc/ 1000000.0; + this->offset = 4000000; + printTree(); + int size = ringNodes.begin()->second.size(); + //第一个桥接边 + int b_size = bridges.size(); + BridgeMap bm = bridges[0]; + IdIndex parent(1, 1); + //遍历 + traverseRing(findNode(bm.from_ii), parent, bm.from, bm.from2, true); + //outputPaths(); + Point_t last = { bm.from.x(),bm.from.y() }; + path.emplace_back(last); + + for (size_t i = 0; i + 1 < path.size(); ++i) { + + //Point p1 = { (path[i].x()+200)* 1000000.0,(path[i].y()+200) * 1000000.0 }; + //Point p2 = { (path[i + 1].x()+200) * 1000000.0,(path[i + 1].y()+200) * 1000000.0 }; + Point p1 = { path[i].x(),path[i].y() }; + Point p2 = { path[i + 1].x() ,path[i + 1].y() }; + Polyline line = { p1, p2 }; + polylines_out.push_back(line); + } + + return polylines_out; + } + + //void Bridge::_fill_surface_single(const FillParams& params, unsigned int thickness_layers, const std::pair& direction, ExPolygon expolygon, Polylines& polylines_out) + //{ + // expolygon.rotate(-direction.first); + // + // this->_min_spacing = scale_(this->spacing); + // assert(params.density > 0.0001f && params.density <= 1.f); + // this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density); + // this->_diagonal_distance = this->_line_spacing * 2; + // this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill + // BoundingBox bounding_box = expolygon.contour.bounding_box(); + // + // // define flow spacing according to requested density + // if (params.density > 0.9999f && !params.dont_adjust) { + // this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); + // this->spacing = unscale(this->_line_spacing); + // } + // else { + // // extend bounding box so that our pattern will be aligned with other layers + // // Transform the reference point to the rotated coordinate system. + // bounding_box.merge(align_to_grid( + // bounding_box.min, + // Point(this->_line_spacing, this->_line_spacing), + // direction.second.rotated(-direction.first))); + // } + // Polygon outPolygon = bounding_box.polygon(); + // Polygon_t polygon; + // std::vector outerBoundary; + // outerBoundary.reserve(outPolygon.points.size()); + // for (Point one : outPolygon.points) { + // double x = one.x(); + // double y = one.y(); + // Point_t temp = { x,y }; + // outerBoundary.push_back(temp); + // } + // bg::append(polygon.outer(), outerBoundary); + // + // //std::vector innerBoundary; + // + // Polygons inPolygons = expolygon.holes; + // for (Polygon one :inPolygons) { + // Ring innerRing; + // for (Point two: one.points) { + // double x = two.x(); + // double y = two.y(); + // Point_t temp = { x,y }; + // innerRing.push_back(temp); + // } + // polygon.inners().push_back(innerRing); + // } + // this->polygon = polygon; + // this->offset = float(scale_(this->overlap - 0.5 * this->spacing)); + // printTree(); + // for (size_t i = 0; i + 1 < path.size(); ++i) { + // + // Point p1 = { path[i].x(),path[i].y()}; + // Point p2 = { path[i + 1].x(),path[i + 1].y()}; + // Polyline line = { p1, p2 }; + // polylines_out.push_back(line); + // } + //} + + /*void Bridge::_fill_surface_single(const FillParams& params, unsigned int thickness_layers, const std::pair& direction, ExPolygon expolygon, Polylines& polylines_out) + { + + + //this->_min_spacing = scale_(this->spacing); + //assert(params.density > 0.0001f && params.density <= 1.f); + //this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density); + //this->_diagonal_distance = this->_line_spacing * 2; + //this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill + //BoundingBox bounding_box = expolygon.contour.bounding_box(); + + //// define flow spacing according to requested density + ////根据要求的密度定义流间距 + //if (params.density > 0.9999f && !params.dont_adjust) { + // this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); + // this->spacing = unscale(this->_line_spacing); + //} + //else { + // // extend bounding box so that our pattern will be aligned with other layers + // // Transform the reference point to the rotated coordinate system. + // //扩展边界框,使我们的图案与其他层对齐 + // //将参考点变换到旋转坐标系。 + // bounding_box.merge(align_to_grid( + // bounding_box.min, + // Point(this->_line_spacing, this->_line_spacing), + // direction.second.rotated(-direction.first))); + //} + + BoundingBox bounding_box = expolygon.contour.bounding_box(); + Polygon outPolygon = bounding_box.polygon(); + Polygon_t polygon; + std::vector outerBoundary; + outerBoundary.reserve(outPolygon.points.size()); + for (Point one : outPolygon.points) { + double x = one.x(); + double y = one.y(); + Point_t temp = { x,y }; + outerBoundary.push_back(temp); + } + bg::append(polygon.outer(), outerBoundary); + + //std::vector innerBoundary; + + Polygons inPolygons = expolygon.holes; + for (Polygon one : inPolygons) { + Ring innerRing; + innerRing.reserve(one.points.size()); + for (Point two : one.points) { + double x = two.x(); + double y = two.y(); + Point_t temp = { x,y }; + innerRing.push_back(temp); + } + polygon.inners().push_back(innerRing); + } + int inSize = polygon.inners().size(); + // Bridge bridge(polygon, float(scale_(this->overlap - 0.5 * this->spacing))); + this->polygon = polygon; + this->offset = float(scale_(this->overlap - 0.5 * this->spacing)); + printTree(); + //outputPaths(); + + for (size_t i = 0; i + 1 < path.size(); ++i) { + + Point p1 = { path[i].x(),path[i].y()}; + Point p2 = { path[i + 1].x(),path[i + 1].y()}; + Polyline line = { p1, p2 }; + polylines_out.push_back(line); + } + }*/ + + +bool is_point_on_segment(const Point_t& p, const Segment& seg, double tolerance = 1e-6) { + // 计算点到线段的距离 + double dist = bg::distance(p, seg); + + // 检查距离是否在容差范围内 + if (dist > tolerance) { + return false; + } + + // 获取线段端点 + const Point_t& p1 = seg.first; + const Point_t& p2 = seg.second; + + // 检查点是否在线段的边界框内 + double min_x = std::min(p1.x(), p2.x()); + double max_x = std::max(p1.x(), p2.x()); + double min_y = std::min(p1.y(), p2.y()); + double max_y = std::max(p1.y(), p2.y()); + + // 考虑容差 + return (p.x() >= min_x - tolerance) && (p.x() <= max_x + tolerance) && + (p.y() >= min_y - tolerance) && (p.y() <= max_y + tolerance); +} + +// 计算点到原点的距离平方 +double distance_to_origin_sq(const Point_t& p) { + return bg::get<0>(p) * bg::get<0>(p) + bg::get<1>(p) * bg::get<1>(p); +} + +// 找到环中距离原点最近的点作为起点 +void reorder_ring_start_point(Ring& ring) { + + // 找到距离原点最近的点 + auto min_it = std::min_element(ring.begin(), ring.end(), + [](const Point_t& a, const Point_t& b) { + return distance_to_origin_sq(a) < distance_to_origin_sq(b); + }); + + // 旋转环使最近的点成为起点 + std::rotate(ring.begin(), min_it, ring.end() - 1); // end()-1 保留闭合点 + + // 确保环保持闭合 + if (!bg::equals(ring.front(), ring.back())) { + ring.back() = ring.front(); + } +} + + + +//将节点加入处理节点集合中 +RingNode Bridge::formatNode(const size_t id, const size_t index, const Ring& ring, const int orientation) { + Ring ringNode; + ringNode.assign(ring.begin(), ring.end()); + RingNode node({ id, index }, ringNode, orientation); + return node; +} +void Bridge::addNode(RingNode& node) { + reorder_ring_start_point(node.ring); + // 检查键是否存在 + const auto it = ringNodes.find(node.id.id); + if (it != ringNodes.end()) { + // 键已存在,将新节点添加到对应向量的末尾 + it->second.emplace_back(node); + } + else { + // 键不存在,创建一个新的向量并插入到 map 中 + ringNodes[node.id.id] = { node }; + } +} + +void Bridge::removeNode(IdIndex id) { + auto it = ringNodes.find(id.id); + if (it != ringNodes.end()) { + std::vector& nodes = it->second; + nodes.erase(std::remove_if(nodes.begin(), nodes.end(), + [id](RingNode x) { return x.id.id == id.id && x.id.index == id.index; }), + nodes.end()); + } +} + +//查找节点 +RingNode& Bridge::findNode(const IdIndex ii) { + const auto it = ringNodes.find(ii.id); + std::vector& nodes = it->second; + const auto it1 = std::find_if(nodes.begin(), nodes.end(), [ii](const RingNode& node) { + return node.id.index == ii.index; + }); + return *it1; +} + + +//计算多边形的外边界和内边界 +void Bridge::computePolygonBoundaries(const Polygon_t& polygon) { + // 提取外边界 + boundaries.outerBoundary = bg::exterior_ring(polygon); + // 获取内边界 + const auto& interiors = bg::interior_rings(polygon); + for (const auto & inner : interiors) { + boundaries.innerBoundaries.push_back(inner); + } +} + +// 判断两个环是否相交(不包括包含关系) +bool Bridge::ringsIntersect(const Ring& ring1, const Ring& ring2) +{ + // 首先检查是否相交(包括边界相交) + if (!bg::intersects(ring1, ring2)) { + return false; + } + + // 然后排除包含关系 + bool ring1_inside_ring2 = bg::within(ring1, ring2); + bool ring2_inside_ring1 = bg::within(ring2, ring1); + + // 如果存在包含关系,则不算作相交 + if (ring1_inside_ring2 || ring2_inside_ring1) { + return false; + } + + return true; +} + + +//获取环偏移后的环(可能为空) +std::vector Bridge::offsetRing(const Ring& ring, const double distance) { + // 将环包装成多边形 + Polygon_t inputPolygon; + inputPolygon.outer() = ring; + // 修复几何数据 + bg::correct(inputPolygon); + + // 使用 multi_polygon 作为 buffer 的输出类型 + bg::model::multi_polygon result; + // 使用对称距离策略 + bg::buffer(inputPolygon, result, + bg::strategy::buffer::distance_symmetric(distance), + bg::strategy::buffer::side_straight(), + bg::strategy::buffer::join_round(), + bg::strategy::buffer::end_round(), + bg::strategy::buffer::point_circle()); + // 提取偏移后的环 + std::vector offsetRing; + for (const auto& poly : result) { + offsetRing.emplace_back(poly.outer()); + } + return offsetRing; +} + +// 查找环非邻边的相交点 +std::vector Bridge::findNonAdjacentIntersections(const Ring& ring) { + Polygon_t poly; + bg::append(poly.outer(), ring); + std::vector intersections; + std::vector> segments; + const size_t num_points = bg::num_points(poly.outer()); + + // 构建所有边的线段 + for (int i = 0; i < num_points - 1; ++i) { + segments.emplace_back(poly.outer()[i], poly.outer()[i + 1]); + } + + // 遍历所有非邻边对 + for (size_t i = 0; i < segments.size(); ++i) { + for (size_t j = i + 2; j < segments.size(); ++j) { + if ((i == 0 && j == segments.size() - 1)) continue; // 排除相邻边 + std::vector intersection_points; + bg::intersection(segments[i], segments[j], intersection_points); + + for (const auto& point : intersection_points) { + // 检查相交点是否为多边形的顶点 + bool is_vertex = false; + for (const auto& vertex : polygon.outer()) { + if (bg::equals(point, vertex)) { + is_vertex = true; + break; + } + } + if (!is_vertex) { + intersections.push_back(point); + } + } + } + } + return intersections; +} + +// 将环拆分成多个环 +std::vector Bridge::split(const Ring& ring, const std::vector& intersections) { + std::vector rings; + std::vector visited(ring.size(), false); + Ring extendedRing = ring; + + // 插入相交点到环中 + for (const auto& intersection : intersections) { + for (size_t i = 0; i < ring.size(); ++i) { + Segment seg(ring[i], ring[(i + 1) % ring.size()]); + if (bg::within(intersection, seg)) { + extendedRing.insert(extendedRing.begin() + i + 1, intersection); + break; + } + } + } + + std::vector newVisited(extendedRing.size(), false); + for (size_t i = 0; i < extendedRing.size(); ++i) { + if (newVisited[i]) continue; + + Ring newRing; + size_t current = i; + do { + newRing.push_back(extendedRing[current]); + newVisited[current] = true; + current = (current + 1) % extendedRing.size(); + } while (current != i); + + if (newRing.size() > 2) { + rings.push_back(newRing); + } + } + return rings; +} + + +// 在环的指定位置插入点 +void insert_point_in_ring(Ring& ring, const Point_t& point, size_t after_index) { + auto it = ring.begin(); + std::advance(it, after_index + 1); + ring.insert(it, point); +} + +// 找到环上应该插入交点的位置 +std::vector> find_insertion_points(const Ring& ring, const std::vector& intersections) { + std::vector> insertion_points; + + for (const auto& p : intersections) { + double min_dist = std::numeric_limits::max(); + size_t best_segment = 0; + + for (size_t i = 0; i < ring.size() - 1; ++i) { + Point_t seg_start = ring[i]; + Point_t seg_end = ring[i + 1]; + + // 计算点到线段的距离 + double dist = bg::distance(p, bg::model::segment(seg_start, seg_end)); + + if (dist < min_dist) { + min_dist = dist; + best_segment = i; + } + } + + if (min_dist < 1e-6) { // 使用小的容差值 + insertion_points.emplace_back(best_segment, p); + } + } + + // 按插入位置排序,从后往前插入 + std::sort(insertion_points.begin(), insertion_points.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + + return insertion_points; +} + +// 合并两个环 +std::vector Bridge::merge(const RingNode& ring1, const RingNode& ring2) { + + // 将环转换为多边形 + Polygon_t poly1, poly2; + bg::append(poly1.outer(), ring1.ring); + bg::append(poly2.outer(), ring2.ring); + + bg::correct(poly1); // 确保多边形是有效的 + bg::correct(poly2); // 确保多边形是有效的 + + std::vector intersection_points; + bg::intersection(poly1, poly2, intersection_points); + + // 将交点插入到两个多边形中 + auto insertions1 = find_insertion_points(poly1.outer(), intersection_points); + for (const auto& ins : insertions1) { + insert_point_in_ring(poly1.outer(), ins.second, ins.first); + } + + auto insertions2 = find_insertion_points(poly2.outer(), intersection_points); + for (const auto& ins : insertions2) { + insert_point_in_ring(poly2.outer(), ins.second, ins.first); + } + + + // 计算多边形交、差布尔运算 + /* MultiPolygon mp0; + bg::intersection(poly1, poly2, mp0);*/ + + MultiPolygon mp1; + bg::difference(poly1, poly2, mp1); + + MultiPolygon mp2; + bg::difference(poly2,poly1, mp2); + + // 合并所有结果多边形 + MultiPolygon result; + //result.insert(result.end(), mp0.begin(), mp0.end()); + if (ring1.orientation == -1 && ring2.orientation == 1) { + result.insert(result.end(), mp1.begin(), mp1.end()); + } + else if(ring1.orientation == 1 && ring2.orientation == -1){ + result.insert(result.end(), mp2.begin(), mp2.end()); + } + else { + result.insert(result.end(), mp1.begin(), mp1.end()); + result.insert(result.end(), mp2.begin(), mp2.end()); + } + + std::vector allRings; + + for (const auto& poly : result) { + allRings.emplace_back(poly.outer()); + } + + //for (const auto& poly : result) { + // allRings.push_back(poly.outer()); + // for (const auto& inner : poly.inners()) { + // allRings.push_back(inner); + // } + //} + + //// 过滤掉被其他环完全包含的环 + std::vector disjointRings; + for (size_t i = 0; i < allRings.size(); ++i) { + bool isContained = false; + for (size_t j = 0; j < allRings.size(); ++j) { + if (i != j && bg::within(allRings[i], allRings[j])) { + isContained = true; + break; + } + } + if (!isContained) { + disjointRings.push_back(allRings[i]); + } + } + return allRings; +} + +//生成环集 +void Bridge::generateRings() { + //获取多边形的外边界和内边界 + computePolygonBoundaries(polygon); + + //外边界 + std::vector rings = offsetRing(boundaries.outerBoundary, -offset / 2); + for (auto& r : rings) { + RingNode node = formatNode(maxRid++, 1, r, -1); + nodeHandles.emplace_back(node); + addNode(node); + } + + //内边界 偏移 + std::map offRings; //存放待偏移的环 + for (auto& inner : boundaries.innerBoundaries) { + offRings.insert({{maxRid++,1},inner }); + } + std::vector tempRings; + for (auto& r : offRings) { + std::vector _rings = offsetRing(r.second, offset / 2); + if (_rings.size() == 1) { + RingNode node1 = formatNode(r.first.id, r.first.index + 1, _rings[0], 1); + if (bg::area(node1.ring) > area_threshold) { + addNode(node1); + tempRings.emplace_back(node1); + } + } + else if (_rings.size() > 1) { + std::vector id_indices; + for (auto& r1 : _rings) { + RingNode node0 = formatNode(++maxRid, 1, r1, 1); + if (bg::area(node0.ring) > area_threshold) { + addNode(node0); + tempRings.emplace_back(node0); + IdIndex ii(maxRid, 1); + id_indices.emplace_back(ii); + } + } + //分裂映射 + offsetMap.insert(std::pair>({ r.first.id,r.first.index }, id_indices)); + } + } + nodeHandles.insert(nodeHandles.end(), tempRings.begin(), tempRings.end()); + + //遍历当前用于判断自/互交的节点集 + while (!nodeHandles.empty()) { + std::vector interNodes; //产生互交的节点集 + std::vector nodes; //互交后产生的节点 + for (auto it1 = nodeHandles.begin(); it1 != nodeHandles.end(); ++it1) { + //互交判断&处理 + auto it2 = std::next(it1); + for (;it2 != nodeHandles.end(); ++it2) { + if (ringsIntersect(it1->ring, it2->ring)) { + //合并 + std::vector rings = merge(*it1, *it2); + std::vector id_indices; + for (const auto& ring2 : rings) { + // 外与内 外与外 -> 向内; 内与内 -> 向外 + const int orientation = (it1->orientation + it2->orientation) <= 0 ? -1 : 1; + RingNode node1 = formatNode(++maxRid, 1, ring2, orientation); + if (bg::area(node1.ring) > area_threshold) { + nodes.emplace_back(node1); + addNode(node1); + IdIndex _ii(maxRid, 1); + id_indices.emplace_back(_ii); + } + } + MergeMap mm(it1->id, it2->id, id_indices); + mergeMap.emplace_back(mm); + interNodes.emplace_back(*it1); + interNodes.emplace_back(*it2); + it1++; + break; + } + } + } + //删除互交节点 + for (auto& n : interNodes) { + nodeHandles.erase(std::remove(nodeHandles.begin(), nodeHandles.end(), n), nodeHandles.end()); + } + + //是否存在互交 + if (!nodes.empty()) { + for (auto& n : nodes) { + nodeHandles.emplace_back(n); + } + } + std::vector offsetNodes; + //偏移 + for (auto& node : nodeHandles) { + //if (node.orientation == 1) continue; + std::vector ring0 = offsetRing(node.ring, static_cast(node.orientation) * offset); + if (ring0.size() == 1) { + RingNode node0 = formatNode(node.id.id, ++node.id.index, ring0[0], node.orientation); + if (bg::area(node0.ring) > area_threshold) { + addNode(node0); + offsetNodes.emplace_back(node0); + } + } + else if (ring0.size() > 1) { + std::vector id_indices; + for (auto& r : ring0) { + RingNode node0 = formatNode(++maxRid, 1, r, node.orientation); + if (bg::area(node0.ring) > area_threshold) { + addNode(node0); + offsetNodes.emplace_back(node0); + IdIndex ii(maxRid, 1); + id_indices.emplace_back(ii); + } + } + //分裂映射 + offsetMap.insert(std::pair>({node.id.id,node.id.index }, id_indices)); + } + } + //清空 + nodeHandles.clear(); + if (!offsetNodes.empty()) { + //重填 + nodeHandles.insert(nodeHandles.end(), offsetNodes.begin(), offsetNodes.end()); + } + } +} + +//形成树 +void Bridge::formatTree() { + //遍历不同环类型的节点集合 + for (auto& ringNode : ringNodes) { + // 使用反向迭代器进行反序遍历 + std::vector& vec = ringNode.second; + const auto it0 = ringNode.second.begin(); + if (it0->orientation == 1) { // 向外 倒序 + for (int i = ringNode.second.size() - 1; i > 0; i--) { + ringNode.second[i].children.emplace_back(ringNode.second[i - 1]); + ringNode.second[i - 1].parent = &ringNode.second[i]; + } + } + else { //向内 正序 + for (int i = 0; i < vec.size() - 1; i++) { + ringNode.second[i].children.emplace_back(ringNode.second[i + 1]); + ringNode.second[i + 1].parent = &ringNode.second[i]; + } + } + } + + //遍历分裂/合并映射 + for (auto& map : offsetMap) { //偏移产生的分裂 + RingNode& it1 = findNode(map.first); + for (const auto& ii : map.second) { + RingNode& it2 = findNode(ii); + it1.children.emplace_back(it2); + } + } + for (auto& map : mergeMap) { //合并 + RingNode& it1 = findNode(map.ii1); + RingNode& it2 = findNode(map.ii2); + for (const auto& ii : map.nodes) { + RingNode& it = findNode(ii); + + if (it.orientation == -1) { //向内 + //内 外 + if (it1.orientation == -1 && it2.orientation == 1) { + it1.parent->children.emplace_back(it); + it.parent = it1.parent; + IdIndex _ii = it1.id; + + //将it1从父节点的子节点集中移除 + it1.parent->children.erase(std::remove_if(it1.parent->children.begin(), it1.parent->children.end(), + [_ii](RingNode x) { return x.id.id == _ii.id && x.id.index == _ii.index; }), + it1.parent->children.end()); + //it作为it2子节点的父节点 + for (auto& c : it2.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it2.children.begin(), it2.children.end()); + } + else if (it1.orientation == 1 && it2.orientation == -1) //外 内 + { + it2.parent->children.emplace_back(it); + it.parent = it2.parent; + IdIndex _ii = it2.id; + //将it2从父节点的子节点集中移除 + it2.parent->children.erase(std::remove_if(it2.parent->children.begin(), it2.parent->children.end(), + [_ii](RingNode x) { return x.id.id == _ii.id && x.id.index == _ii.index; }), + it2.parent->children.end()); + //it作为it1子节点的父节点 + for (auto& c : it1.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it1.children.begin(), it1.children.end()); + } + else if (it1.orientation == 1 && it2.orientation == 1) { //外 外 + //it作为it1子节点的父节点 + for (auto& c : it1.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it1.children.begin(), it1.children.end()); + //it作为it2子节点的父节点 + for (auto& c : it2.children) { + c.parent = ⁢ + } + it.children.insert(it.children.end(), it2.children.begin(), it2.children.end()); + } + else if (it1.orientation == -1 && it2.orientation == -1) {// 内 内 + it1.parent->children.emplace_back(it); + //it.parent = it1.parent; + IdIndex _ii = it1.id; + //将it1从父节点的子节点集中移除 + it1.parent->children.erase(std::remove_if(it1.parent->children.begin(), it1.parent->children.end(), + [_ii](RingNode x) { return x.id.id == _ii.id && x.id.index == _ii.index; }), + it1.parent->children.end()); + + it2.parent->children.emplace_back(it); + //it.parent = it2.parent; + IdIndex _ii2 = it2.id; + //将it2从父节点的子节点集中移除 + it2.parent->children.erase(std::remove_if(it2.parent->children.begin(), it2.parent->children.end(), + [_ii2](RingNode x) { return x.id.id == _ii2.id && x.id.index == _ii2.index; }), + it2.parent->children.end()); + } + } + else { //向外 + //it1.parent = ⁢ + //将it1的子节点作为it的子节点 + it.children.insert(it.children.end(), it1.children.begin(), it1.children.end()); + it1.children.clear(); //清空it1的子节点集 + //it2.parent = ⁢ + it.children.insert(it.children.end(), it2.children.begin(), it2.children.end()); + it2.children.clear(); + } + + RingNode* it_parent = it.parent; + //判断是否互交 + if (ringsIntersect(it.ring, it_parent->ring)) { + //合并 + std::vector rings = merge(it1, *it1.parent); + const int orientation = (it1.orientation + it1.parent->orientation) <= 0 ? -1 : 1; + RingNode node1 = formatNode(++maxRid, 1, rings[0], orientation); + if (bg::area(node1.ring) > area_threshold) { + addNode(node1); + + findNode(node1.id).children.insert(findNode(node1.id).children.end(), it1.children.begin(), it1.children.end()); + if (it_parent->parent != nullptr) { + IdIndex ii = it_parent->id; + it_parent->parent->children.erase(std::remove_if(it_parent->parent->children.begin(), it_parent->parent->children.end(), + [ii](RingNode x) { return x.id.id == ii.id && x.id.index == ii.index; }), + it_parent->parent->children.end()); + it_parent->parent->children.emplace_back(node1); + } + } + } + } + } +} + +//树遍历 +//void Bridge::dfs(RingNode& node, std::unordered_set>& visited, size_t& edge) { +// if (visited.find({ node.id.id, node.id.index }) != visited.end()) { +// return; +// } +// visited.insert({ node.id.id, node.id.index }); +// edge++; +// // 处理当前节点 +// std::cout << "rId: " << node.id.id << ", rIndex: " << node.id.index +// << ", orientation: " << node.orientation +// << ", isHide: " << (node.isHide ? "true" : "false") << std::endl; +// +// Polygon poly; +// bg::assign(poly, node.ring); +// verteies.push_back(convertToSFML(poly, sf::Color::Red)); +// +// // 递归遍历子节点 +// for (auto& child : node.children) { +// handleBridge(node.id, child.id, child.ring.size(),edge); +// dfs(findNode(child.id), visited, edge); +// } +//} +void Bridge::dfs(RingNode& node, std::vector& visited, size_t& edge) { + auto& it = std::find_if(visited.begin(), visited.end(), [node](const IdIndex& ii) { + return ii.id == node.id.id && ii.index == node.id.index; + }); + if (it != visited.end()) { + return; + } + visited.emplace_back(node.id); + edge++; + // 处理当前节点 + std::cout << "rId: " << node.id.id << ", rIndex: " << node.id.index + << ", orientation: " << node.orientation + << ", isHide: " << (node.isHide ? "true" : "false") << std::endl; + + Polygon_t poly; + bg::assign(poly, node.ring); + //verteies.push_back(convertToSFML(poly, sf::Color::Red)); + + // 递归遍历子节点 + for (auto& child : node.children) { + handleBridge(node.id, child.id, child.ring.size(), edge); + dfs(findNode(child.id), visited, edge); + } +} + + +//找到多边形环第 N 条边的中心点 +Point_t Bridge::findCenterPointOnEdge(Ring& ring, size_t N) { + + // 获取第 N 条边的两个端点 + const Point_t start = ring[N]; + const Point_t end = ring[N + 1]; + // 计算中点 + const double mid_x = (start.x() + end.x()) / 2; + const double mid_y = (start.y() + end.y()) / 2; + + Point_t p(mid_x, mid_y); + const auto insertPos = ring.begin() + N + 1; + ring.insert(insertPos, p); + + bg::correct(ring); //调整方向为顺时针 + return p; +} + +// 计算点到线段的最近点 +Point_t closest_point_on_segment(const Point_t& p, const Point_t& seg_start, const Point_t& seg_end) { + const double x = bg::get<0>(p); + const double y = bg::get<1>(p); + const double x1 = bg::get<0>(seg_start); + const double y1 = bg::get<1>(seg_start); + const double x2 = bg::get<0>(seg_end); + const double y2 = bg::get<1>(seg_end); + + // 线段向量 + const double dx = x2 - x1; + const double dy = y2 - y1; + + // 如果线段长度为零,返回起点 + if (dx == 0 && dy == 0) { + return seg_start; + } + + // 计算投影参数t + const double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); + + // 根据t值确定最近点 + if (t <= 0.0) { + return seg_start; + } + else if (t >= 1.0) { + return seg_end; + } + else { + return Point_t(x1 + t * dx, y1 + t * dy); + } +} + +// 在环的边上查找离给定点最近的点 +Point_t find_closest_point_on_ring_edges(Ring& ring, const Point_t& p0) { + // 验证环是否闭合 + if (ring.size() < 2 || !bg::equals(ring.front(), ring.back())) { + throw std::runtime_error("Ring is not closed"); + } + + Point_t closest_point; + double min_distance = std::numeric_limits::max(); + + size_t e_index = 0; + // 遍历环的所有边 + for (size_t i = 0; i < ring.size() - 1; ++i) { + const Point_t& p1 = ring[i]; + const Point_t& p2 = ring[i + 1]; + + // 计算线段上的最近点 + Point_t projected_point = closest_point_on_segment(p0, p1, p2); + + // 计算距离 + double distance = bg::distance(p0, projected_point); + + // 更新最近点 + if (distance < min_distance) { + min_distance = distance; + closest_point = projected_point; + e_index = i; + } + } + //插入 + ring.insert(ring.begin()+e_index+1, closest_point); + + bg::correct(ring); //调整方向为顺时针 + return closest_point; +} + +// 找到点所在的边(返回边起点索引) +std::pair find_edge_containing_point(const Ring& ring, const Point_t& point) { + for (size_t i = 0; i < ring.size() - 1; ++i) { + const Point_t& p1 = ring[i]; + const Point_t& p2 = ring[i + 1]; + + // 计算点到线段的距离 + double seg_length = bg::distance(p1, p2); + if (seg_length == 0) continue; // 忽略零长度边 + + // 计算点在边上的投影参数t + double t = ((bg::get<0>(point) - bg::get<0>(p1)) * (bg::get<0>(p2) - bg::get<0>(p1)) + + (bg::get<1>(point) - bg::get<1>(p1)) * (bg::get<1>(p2) - bg::get<1>(p1))) / + (seg_length * seg_length); + + // 如果点在当前边上 + if (t >= 0.0 && t <= 1.0) { + Point_t proj( + bg::get<0>(p1) + t * (bg::get<0>(p2) - bg::get<0>(p1)), + bg::get<1>(p1) + t * (bg::get<1>(p2) - bg::get<1>(p1)) + ); + + // 检查投影点与实际点的距离是否足够近 + if (bg::distance(point, proj) < 1e-6) { + return { i, t }; + } + } + } +} + + +Point_t Bridge::find_point_at_distance_clockwise(Ring& ring, const Point_t& start_point, double d) +{ + // 1. 找到起点所在的边 + std::pair p = find_edge_containing_point(ring, start_point); + size_t edge_index = p.first; + double t = p.second; + // 计算从起点到当前边终点的剩余距离 + const Point_t& p1 = ring[edge_index]; + const Point_t& p2 = ring[(edge_index + 1) % ring.size() ]; + double remaining_on_edge = (1.0 - t) * bg::distance(p1, p2); + + // 3. 如果剩余距离足够 + if (remaining_on_edge >= d) { + double new_t = t + d / bg::distance(p1, p2); + Point_t p(bg::get<0>(p1) + new_t * (bg::get<0>(p2) - bg::get<0>(p1)), + bg::get<1>(p1) + new_t * (bg::get<1>(p2) - bg::get<1>(p1))); + auto insertPos = ring.begin() + edge_index + 1; + ring.insert(insertPos, p); + bg::correct(ring); //调整方向为顺时针 + return p; + } + + // 4. 否则继续遍历后续边 + double accumulated_distance = remaining_on_edge; + size_t current_index = (edge_index + 1) % (ring.size() - 1); + + while (accumulated_distance < d) { + const Point_t& p1 = ring[current_index]; + const Point_t& p2 = ring[(current_index + 1) % (ring.size() - 1)]; + double segment_length = bg::distance(p1, p2); + + if (accumulated_distance + segment_length >= d) { + double ratio = (d - accumulated_distance) / segment_length; + //在current_index 插入 + Point_t p(bg::get<0>(p1) + ratio * (bg::get<0>(p2) - bg::get<0>(p1)), + bg::get<1>(p1) + ratio * (bg::get<1>(p2) - bg::get<1>(p1))); + auto insertPos = ring.begin() + current_index + 1; + ring.insert(insertPos, p); + bg::correct(ring); //调整方向为顺时针 + return p; + } + accumulated_distance += segment_length; + current_index = (current_index + 1) % (ring.size() - 1); + } + return Point_t(); +} + + +// 判断点是否在线段内部(非端点) +bool is_point_inside_segment(const Point_t& p, const Segment& seg) { + double x = bg::get<0>(p); + double y = bg::get<1>(p); + double x1 = bg::get<0>(seg.first); + double y1 = bg::get<1>(seg.first); + double x2 = bg::get<0>(seg.second); + double y2 = bg::get<1>(seg.second); + + // 检查点是否在线段的包围盒内 + if ((x < std::min(x1, x2) - 1e-6) || (x > std::max(x1, x2) + 1e-6) || + (y < std::min(y1, y2) - 1e-6) || (y > std::max(y1, y2) + 1e-6)) { + return false; + } + + // 向量叉积判断共线(可跳过,因为 intersects 已保证相交) + double cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1); + if (std::abs(cross) > 1e-6) return false; + + return true; +} + +// 判断两线段是否在内部相交(排除端点接触) +bool segments_cross(const Segment& seg1, const Segment& seg2) { + if (!bg::intersects(seg1, seg2)) return false; + + std::vector points; + bg::intersection(seg1, seg2, points); + + if (points.empty()) return false; + + // 检查交点是否在两条线段的内部 + for (const auto& p : points) { + if (is_point_inside_segment(p, seg1) && is_point_inside_segment(p, seg2)) { + return true; + } + } + return false; +} + +//在环上寻找长度最大的边索引 +size_t Bridge::findLongestEdgeIndex(const Ring& ring) { + if (ring.size() < 4) { + throw std::invalid_argument("Ring must have at least 4 points"); + } + + std::vector max_indices; + double max_length = 0.0; + const size_t n = ring.size(); + + // 遍历所有边,收集最大长度边的索引 + for (size_t i = 0; i < n - 1; ++i) { + const auto& p1 = ring[i]; + const auto& p2 = ring[i + 1]; + const double current_length = bg::distance(p1, p2); + + if (current_length > max_length) { + max_length = current_length; + max_indices.clear(); + max_indices.push_back(i); + } + else if (current_length == max_length) { + max_indices.push_back(i); + } + } + isFront = !isFront; + if (max_indices.size() > 1) { + sel_edge_index = isFront ? max_indices[0] : max_indices[max_indices.size() - 1]; + } + else { + /*std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1, n);*/ + + sel_edge_index = isFront?(max_indices[0] + 1) % (n - 1):(max_indices[0]-1+n)%(n-1); + } + return sel_edge_index; + +} + +//判断两个点是否相等 +bool equal(Point_t p1, Point_t p2) { + return p1.x() == p2.x() && p1.y() == p2.y(); +} + +//在第N条边上进行内外环桥接,并插入桥接点 +void Bridge::handleBridge(IdIndex o_ii, IdIndex i_ii,const size_t ring_size, const size_t N) +{ + //随机选择桥接的边 + RingNode& inner = findNode(i_ii); + RingNode& outer = findNode(o_ii); + //从内到外 + size_t edge_index = findLongestEdgeIndex(inner.ring); + Point_t p0 = findCenterPointOnEdge(inner.ring, edge_index); + Point_t p1 = find_point_at_distance_clockwise(inner.ring, p0, offset); + Point_t p2 = find_closest_point_on_ring_edges(outer.ring, p0); + Point_t p3 = find_point_at_distance_clockwise(outer.ring, p2, offset); + + //从外到内 + edge_index = findLongestEdgeIndex(outer.ring); + Point_t p2_1 = findCenterPointOnEdge(outer.ring, edge_index); + Point_t p3_1 = find_point_at_distance_clockwise(outer.ring, p2_1, offset); + Point_t p0_1 = find_closest_point_on_ring_edges(inner.ring, p2_1); + Point_t p1_1 = find_point_at_distance_clockwise(inner.ring, p0_1, offset); + + + //判断环上是否存在桥接 + bool isExist = false; + for (auto& b : bridges) { + if (b.to_ii == i_ii) { + isExist = true; + break; + } + } + if (isExist) return; + + if (bg::distance(p1, p2) < bg::distance(p1_1, p2_1)) { + Segment seg1(p2, p1); + Segment seg2(p3, p0); + if (bg::intersects(seg1, seg2)) { + BridgeMap edge(p2, p1, p3, p0, o_ii, i_ii); + bridges.push_back(edge); + } + else { + BridgeMap edge(p2, p0, p3, p1, o_ii, i_ii); + bridges.push_back(edge); + } + } + else { + Segment seg1(p2_1, p1_1); + Segment seg2(p3_1, p0_1); + if (bg::intersects(seg1, seg2)) { + BridgeMap edge(p2_1, p1_1, p3_1, p0_1, o_ii, i_ii); + bridges.push_back(edge); + } + else { + BridgeMap edge(p2_1, p0_1, p3_1, p1_1, o_ii, i_ii); + bridges.push_back(edge); + } + } + + +} + + +//输出所有环 +void Bridge::printRings() { + generateRings(); + // 输出环中的点信息 + std::cout << "Points in the ring:" << std::endl; + for (const auto& map : ringNodes) { + std::cout << "(" << map.first << std::endl; + for (const auto& node : map.second) { + std::cout << "{" << node.id.id << "," << node.id.index << "," << node.orientation << std::endl; + for (const auto& point : node.ring) { + std::cout << "(" << bg::get<0>(point) << "," << bg::get<1>(point) << ")"; + } + std::cout << "}" << std::endl; + } + std::cout << ")" << std::endl; + } +} + +//输出树结构 +void Bridge::printTree() { + generateRings(); + formatTree(); + // 用于记录已访问的节点 + //std::unordered_set> visited; + std::vector visited; + size_t edge = 1; + dfs(findNode({ 1,1 }), visited,edge); + +} + + +//点在环上的位置索引 +int findPointIndex(const Ring& ring, const Point_t& p0) { + for (size_t i = 0; i < ring.size(); ++i) { + if (ring[i].x() == p0.x() && ring[i].y() == p0.y()) { + return static_cast(i); + } + } + return -1; // 未找到 +} + +int findIndex(std::vector points, const Point_t& p0) { + for (int i = 0; i < points.size(); ++i) { + if (equal(points[i], p0)){ + return static_cast(i); + } + } + return -1; // 未找到 +} + + + +//递归遍历环 +void Bridge::traverseRing( + RingNode& node, + IdIndex parent, + Point_t& start, + Point_t& end, + bool isOutermostLayer +) { + //得到start点在环ring上的索引位置 + int size = node.ring.size(); + int s_index = findPointIndex(node.ring, start); + int e_index = findPointIndex(node.ring, end); + std::vector c_points; + std::vector cc_points; + std::vector points; + for (int i = s_index; !equal(node.ring[i], end); i = (i + 1) % size) { + c_points.emplace_back(node.ring[i]); + } + c_points.emplace_back(end); + for (int i = s_index; !equal(node.ring[i], end); i = (i - 1 + size) % size) { + cc_points.emplace_back(node.ring[i]); + } + cc_points.emplace_back(end); + + if (cc_points.size() > c_points.size()) { + points.assign(cc_points.begin(), cc_points.end()); + } + else + { + points.assign(c_points.begin(), c_points.end()); + } + int index = 0; + do { + //转换成SFML 顶点 + /* path.emplace_back(sf::Vertex( + sf::Vector2f(static_cast(points[index].x()), + static_cast(points[index].y())), + sf::Color::Red + ));*/ + path.emplace_back(points[index]); + + BridgeMap* bridge = nullptr; + //判断该点是否为桥接点 + for (auto& bm : bridges) { + if (equal(bm.from, points[index]) || equal(bm.from2, points[index])) { + bridge = &bm; + break; + } + } + if (bridge != nullptr) { + RingNode& rn = findNode(bridge->to_ii); //to 环 + if (equal(bridge->from, points[index])) { + traverseRing(rn, node.id, bridge->to, bridge->to2, false); + index = findIndex(points, bridge->from2); + } + else if (equal(bridge->from2, points[index])) { + traverseRing(rn, node.id, bridge->to2, bridge->to, false); + index = findIndex(points, bridge->from); + } + if (index == -1) + break; + /* path.emplace_back(sf::Vertex( + sf::Vector2f(static_cast(points[index].x()), + static_cast(points[index].y())), + sf::Color::Red + ));*/ + path.emplace_back(points[index]); + } + index += isOutermostLayer ? -1 : 1; + } while (index > 0 && index < points.size()); + + +} + +//画路径 SFML顶点数组集 +//std::vector Bridge::outputPaths() +//{ +// printTree(); +// //第一个桥接边 +// BridgeMap bm = bridges[0]; +// IdIndex parent(1,1); +// //遍历 +// traverseRing(findNode(bm.from_ii), parent, bm.from, bm.from2, true); +// //形成闭环 +// path.emplace_back(sf::Vertex( +// sf::Vector2f(static_cast(bm.from.x()), +// static_cast(bm.from.y())), +// sf::Color::Red +// )); +// return path; +// +//} + + + + + + +} // namespace Slic3r + + diff --git a/libslic3r/Fill/Bridge.hpp b/libslic3r/Fill/Bridge.hpp new file mode 100644 index 0000000..ef09ec8 --- /dev/null +++ b/libslic3r/Fill/Bridge.hpp @@ -0,0 +1,256 @@ +// +// Created by ylzha on 2025/2/25. +// +//#ifndef DBRIDGE_HPP +//#define DBRIDGE_HPP +#ifndef slic3r_Bridge_hpp_ +#define slic3r_Bridge_hpp_ +// 防止头文件被重复包含的预处理器指令 +//#pragma once +#include "../libslic3r.h" +//#include +//#include +//#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#include "FillBase.hpp" + +namespace bg = boost::geometry; + +//#include +namespace Slic3r{ + +class Surface; + +//namespace bg = boost::geometry; + +// 定义点类型 +typedef bg::model::d2::point_xy Point_t; + +//定义线段 +typedef bg::model::segment Segment; + +// 定义多边形类型 +typedef bg::model::polygon Polygon_t; + +// 多边形集合 +typedef bg::model::multi_polygon MultiPolygon; + +// 定义环类型 +typedef bg::model::ring Ring; + +// 定义折线类型(用于表示直线) +//typedef bg::model::linestring Linestring; + +// 定义多边形的外边界和内边界 +struct PolygonBoundaries { + Ring outerBoundary; // 外边界 + std::vector innerBoundaries; // 内边界(可能有多个) +}; + +struct IdIndex { + size_t id{}; + size_t index{}; + // 定义小于运算符 + bool operator<(const IdIndex& other) const { + return id < other.id; + } + IdIndex(const size_t id, const size_t index) : id(id), index(index) {} + + bool operator==(const IdIndex& ii) const { + return id == ii.id && index == ii.index; + } +}; + +//环路径树结构中节点类型 +struct RingNode { + IdIndex id; + Ring ring; //当前环 + int orientation{}; //方向 -1 向内 1 向外 + std::vector children; //子节点集 + RingNode* parent; //父节点 + bool isHide{ false }; //在最终路径中是否呈现 false 呈现 true 不呈现 + + // 构造函数,方便初始化 + RingNode(const IdIndex id, const Ring& ring, const int orientation = 0) + : id(id), ring(ring), orientation(orientation), parent(nullptr) { + } + + // 重载==运算符方便比较 + bool operator==(const RingNode& other) const { + return id.id == other.id.id && id.index == other.id.index; + } + +}; + +//桥接映射 +struct BridgeMap { + Point_t from; + Point_t to; + Point_t from2; + Point_t to2; + IdIndex from_ii; + IdIndex to_ii; + BridgeMap(Point_t from, Point_t to, Point_t from2, Point_t to2,IdIndex from_ii, IdIndex to_ii) : + from(from), to(to), from2(from2), to2(to2),from_ii(from_ii), to_ii(to_ii) { } +}; + +//合并映射 +struct MergeMap { + IdIndex ii1; //环1 + IdIndex ii2; //环2 + std::vector nodes; //产生的环集 + // 构造函数 + MergeMap(const IdIndex& i1, const IdIndex& i2, const std::vector& nodeList) + : ii1(i1), ii2(i2), nodes(nodeList) { + } +}; + + +//struct PointHash_t { +// std::size_t operator()(const Point_t& p) const { +// std::size_t seed = 0; +// boost::hash_combine(seed, bg::get<0>(p)); +// boost::hash_combine(seed, bg::get<1>(p)); +// return seed; +// } +//}; +// +//struct PointEqual_t { +// bool operator()(const Point_t& a, const Point_t& b) const { +// return bg::equals(a, b); +// } +//}; + + +class Bridge : public Fill { +//public: +// Bridge(Polygon _polygon, const double o) : offset(o), polygon(std::move(_polygon)) {}; +// ~Bridge() = default; + +public: + //Slic3r::Fill* clone() const override { return new Bridge(*this); }; + Fill* clone() const override { return new Bridge(*this); }; + //Bridge() {}; + //Bridge(Polygon_t _polygon, const double o) : offset(o), polygon(std::move(_polygon)) {}; + ~Bridge() override = default; + Polylines fill_surface(const Surface* surface, const FillParams& params) override; + +protected: + /*void _fill_surface_single( + const FillParams& params, + unsigned int thickness_layers, + const std::pair& direction, + ExPolygon expolygon, + Polylines& polylines_out) override;*/ + //Polylines fill_surface(const Surface* surface, const FillParams& params) override; + + coord_t _min_spacing; + coord_t _line_spacing; + // distance threshold for allowing the horizontal infill lines to be connected into a continuous path + coord_t _diagonal_distance; + // only for line infill + coord_t _line_oscillation; +private: + //计算多边形的外边界和内边界 + void computePolygonBoundaries(const Polygon_t& polygon); + + // 判断两个环是否相交(不包括包含关系) + bool ringsIntersect(const Ring& ring1, const Ring& ring2); + + //获取环偏移后的环(可能为空) + std::vector offsetRing(const Ring& ring, double distance); + + // 查找环非邻边的相交点 + std::vector findNonAdjacentIntersections(const Ring& ring); + // 将环拆分成多个环 + std::vector split(const Ring& ring, const std::vector& intersections); + // 合并两个环 + std::vector merge(const RingNode& ring1, const RingNode& ring2); + //生成环集 + void generateRings(); + //形成节点 + RingNode formatNode(size_t id, size_t index, const Ring& ring, int orientation); + void addNode(RingNode& node); + void removeNode(IdIndex id); + //查找节点 + RingNode& findNode(IdIndex ii); + //形成树 + void formatTree(); + // 深度优先搜索遍历树 + void dfs(RingNode& node, std::vector& visited,size_t& edge); + + //找到多边形环第 N 条边的中心点 + Point_t findCenterPointOnEdge(Ring& ring, size_t N); + + // 在环上按顺时针方向查找距离给定点d的另一个点 + Point_t find_point_at_distance_clockwise(Ring& ring, const Point_t& start_point, double d); + + size_t findLongestEdgeIndex(const Ring& ring); + + //在第N条边上进行内外环桥接,并插入桥接点 + //o_ii 外环IdIndex i_ii 内环IdIndex ring_size内环大小 N边索引 + void handleBridge(IdIndex o_ii, IdIndex i_ii, const size_t ring_size, const size_t N); + + //递归遍历环 + void traverseRing( + RingNode& node, + IdIndex parent, + Point_t& start, + Point_t& end, + bool isOutermostLayer //是否最外层 + ); + + //输出所有环 + void printRings(); + + //sf::VertexArray convertToSFML(const Polygon& poly, const sf::Color& color); + + + + +public: + + //输出树结构 + void printTree(); + //std::vector outputPaths(); + //画路径 SFML顶点数组集 + //std::vector outputPaths(); + + //std::vector convert2vertex(); + //std::vector verteies; + //std::vector convertBridge(); + +private: + + size_t maxRid = 1; //最大环类型ID值 + double offset; //偏移距离 + Polygon_t polygon; //2D多边形 + double area_threshold = 4; //面积阈值 + + + PolygonBoundaries boundaries; //外/内边界 + std::map> ringNodes; //存储不同环类型的节点集合 size_t 环类型id + std::vector nodeHandles; //当前需进行自交或互交处理的节点集合 + std::map> splitMap; //分裂映射集 + std::vector mergeMap; //合并映射集 + std::map> offsetMap; //偏移后产生的分裂映射集 + + std::vector bridges; //桥接映射 + + std::vector path; // 路径 + + bool isFront{ false }; + size_t sel_edge_index{ 1 }; //选择桥接边的索引 + + +}; +}; // namespace Slic3r +//#endif //DBRIDGE_HPP +#endif // slic3r_Bridge_hpp_ diff --git a/libslic3r/Fill/Fill.cpp b/libslic3r/Fill/Fill.cpp index 00ae52f..e383c12 100644 --- a/libslic3r/Fill/Fill.cpp +++ b/libslic3r/Fill/Fill.cpp @@ -428,7 +428,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: std::vector surface_fills = group_fills(*this); const Slic3r::BoundingBox bbox = this->object()->bounding_box(); const auto resolution = this->object()->print()->config().resolution.value; - + //bbox.area(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { static int iRun = 0; @@ -437,6 +437,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ for (SurfaceFill &surface_fill : surface_fills) { + //surface_fill.expolygons.contour(). // Create the filler object. //创建填充对象。 std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); @@ -505,6 +506,12 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: //填充物会修改间距以表示调整。为每个expolygon重置它。 f->spacing = surface_fill.params.spacing; surface_fill.surface.expolygon = std::move(expoly); + //expoly.contour.bounding_box().polygon().points; + //for (auto p : surface_fill.surface.expolygon.contour.bounding_box().polygon().points) { + //wss << L"Point(" << p.x() << L", " << p.y() << L"),\n"; + //strstrm << "Point(" << p.x() << ", " << p.y() << "),\n"; + //strstrm << p.x() << "," << p.y() << "\n"; + //} // BBS: make fill //此处往里执行 f->fill_surface_extrusion(&surface_fill.surface, diff --git a/libslic3r/Fill/FillBase.cpp b/libslic3r/Fill/FillBase.cpp index dab5456..906cc64 100644 --- a/libslic3r/Fill/FillBase.cpp +++ b/libslic3r/Fill/FillBase.cpp @@ -24,6 +24,7 @@ // BBS: new infill pattern header #include "FillConcentricInternal.hpp" #include "FillCrossHatch.hpp" +#include "Bridge.hpp" // #define INFILL_DEBUG_OUTPUT @@ -57,7 +58,7 @@ Fill* Fill::new_from_type(const InfillPattern type) // BBS: for bottom and top surface only case ipMonotonicLine: return new FillMonotonicLineWGapFill(); //xiamian+ - case ipFiberSpiral: return new FillLine(); + case ipFiberSpiral: return new Bridge(); default: throw Slic3r::InvalidArgument("unknown type"); } } @@ -133,15 +134,45 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para catch (InfillFailedException&) {} if (!polylines.empty() || !thick_polylines.empty()) { - if (!pointMap.empty()) { - Point tempPoint = pointMap.at(1); - Polyline firstPolyline = polylines.front(); - firstPolyline.append_before(tempPoint); - //Point firstPoint = firstPolyline.first_point(); - } - Polyline lastPolyline = polylines.back(); - Point lastPoint = lastPolyline.last_point(); - pointMap.insert(std::make_pair(1, lastPoint)); + //int key = 1; + //tbb::concurrent_hash_map::const_accessor accessor; + //if (!polylines.empty()) { + // for (auto& one : polylines) { + // //if (!pointMap.empty()) { + // tbb::concurrent_hash_map::const_accessor accessor1; + // if (pointMap.find(accessor1, key)) { + // //Point tempOne = pointMap.at(1); + // Point tempOne = accessor1 -> second; + // Point firstOne = one.first_point(); + // double a1 = (firstOne - tempOne).norm(); + // double a2 = (tempOne - firstOne).norm(); + // if (a1 > 0 || a2 > 0) { + // one.append_before(tempOne); + // } + // } + // Point lastOne = one.last_point(); + // pointMap.emplace(key, lastOne); + // } + //} + //if (!thick_polylines.empty()) { + // for (auto& two : thick_polylines) { + // //if (!pointMap.empty()) { + // tbb::concurrent_hash_map::const_accessor accessor2; + // if (pointMap.find(accessor2,key)){ + // //if (it != pointMap.end()) { + // //Point tempTwo = pointMap.at(1); + // Point tempTwo = accessor2 -> second; + // Point firstTwo = two.first_point(); + // double b1 = (firstTwo - tempTwo).norm(); + // double b2 = (tempTwo - firstTwo).norm(); + // if (b1 > 100 || b2 > 100) { + // two.append_before(tempTwo); + // } + // } + // Point lastTwo = two.last_point(); + // pointMap.emplace(key, lastTwo); + // } + //} // calculate actual flow from spacing (which might have been adjusted by the infill // pattern generator) //根据间距计算实际流量(可能已由填充生成器调整) @@ -188,6 +219,8 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para // This function possibly increases the spacing, never decreases, // and for a narrow width the increase in spacing may become severe, // therefore the adjustment is limited to 20% increase. +//计算一个新的间距,用可能的整数行填充宽度,第一行和最后一行以间隔结束为中心。 +//此功能可能会增加间距,但不会减少,对于窄宽度,间距的增加可能会变得严重,因此调整仅限于20%的增加。 coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance) { assert(width >= 0); @@ -200,6 +233,7 @@ coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance) const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); assert(factor > 1. - 1e-5); // How much could the extrusion width be increased? By 20%. + //挤压宽度可以增加多少?20%。 const coordf_t factor_max = 1.2; if (factor > factor_max) distance_new = coord_t(floor((coordf_t(distance) * factor_max + 0.5))); diff --git a/libslic3r/Fill/FillBase.hpp b/libslic3r/Fill/FillBase.hpp index 015291b..21786f8 100644 --- a/libslic3r/Fill/FillBase.hpp +++ b/libslic3r/Fill/FillBase.hpp @@ -6,7 +6,7 @@ #include #include #include - +#include #include #include "../libslic3r.h" @@ -20,6 +20,7 @@ #include "../ExtrusionEntity.hpp" #include "../ExtrusionEntityCollection.hpp" + namespace Slic3r { class Surface; @@ -106,7 +107,8 @@ public: // BBS: all no overlap expolygons in same layer ExPolygons no_overlap_expolygons; - std::map pointMap; + //std::map pointMap; + tbb::concurrent_hash_map pointMap; public: virtual ~Fill() {} diff --git a/libslic3r/Fill/FillConcentric.cpp b/libslic3r/Fill/FillConcentric.cpp index 8595b1e..e662a8f 100644 --- a/libslic3r/Fill/FillConcentric.cpp +++ b/libslic3r/Fill/FillConcentric.cpp @@ -17,6 +17,7 @@ void FillConcentric::_fill_surface_single( Polylines &polylines_out) { // no rotation is supported for this infill pattern + //此填充图案不支持旋转 BoundingBox bounding_box = expolygon.contour.bounding_box(); coord_t min_spacing = scale_(this->spacing); @@ -36,9 +37,11 @@ void FillConcentric::_fill_surface_single( // generate paths from the outermost to the innermost, to avoid // adhesion problems of the first central tiny loops + //生成从最外层到最内层的路径,以避免第一个中心微环的粘附问题 loops = union_pt_chained_outside_in(loops); // split paths using a nearest neighbor search + //使用最近邻搜索分割路径 size_t iPathFirst = polylines_out.size(); Point last_pos(0, 0); for (const Polygon &loop : loops) { @@ -48,6 +51,7 @@ void FillConcentric::_fill_surface_single( // clip the paths to prevent the extruder from getting exactly on the first point of the loop // Keep valid paths only. + //剪切路径以防止挤出机精确地到达回路的第一点。仅保留有效路径。 size_t j = iPathFirst; for (size_t i = iPathFirst; i < polylines_out.size(); ++ i) { polylines_out[i].clip_end(this->loop_clipping); @@ -74,6 +78,7 @@ void FillConcentric::_fill_surface_single(const FillParams& params, assert(this->print_config != nullptr && this->print_object_config != nullptr); // no rotation is supported for this infill pattern + //此填充图案不支持旋转 Point bbox_size = expolygon.contour.bounding_box().size(); coord_t min_spacing = scaled(this->spacing); @@ -102,6 +107,7 @@ void FillConcentric::_fill_surface_single(const FillParams& params, } // Split paths using a nearest neighbor search. + //使用最近邻搜索分割路径。 size_t firts_poly_idx = thick_polylines_out.size(); Point last_pos(0, 0); for (const Arachne::ExtrusionLine* extrusion : all_extrusions) { @@ -123,6 +129,7 @@ void FillConcentric::_fill_surface_single(const FillParams& params, // clip the paths to prevent the extruder from getting exactly on the first point of the loop // Keep valid paths only. + //夹紧路径,防止挤出机精确地到达环路的第一点 仅保留有效路径。 size_t j = firts_poly_idx; for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) { thick_polylines_out[i].clip_end(this->loop_clipping); diff --git a/libslic3r/Fill/FillLine.cpp b/libslic3r/Fill/FillLine.cpp index b7ab543..e24599c 100644 --- a/libslic3r/Fill/FillLine.cpp +++ b/libslic3r/Fill/FillLine.cpp @@ -15,6 +15,7 @@ void FillLine::_fill_surface_single( Polylines &polylines_out) { // rotate polygons so that we can work with vertical lines here + //旋转多边形,这样我们就可以在这里处理垂直线 expolygon.rotate(- direction.first); this->_min_spacing = scale_(this->spacing); @@ -25,12 +26,15 @@ void FillLine::_fill_surface_single( BoundingBox bounding_box = expolygon.contour.bounding_box(); // define flow spacing according to requested density + //根据要求的密度定义流间距 if (params.density > 0.9999f && !params.dont_adjust) { this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); this->spacing = unscale(this->_line_spacing); } else { // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. + //扩展边界框,使我们的图案与其他层对齐 + //将参考点变换到旋转坐标系。 bounding_box.merge(align_to_grid( bounding_box.min, Point(this->_line_spacing, this->_line_spacing), @@ -38,6 +42,7 @@ void FillLine::_fill_surface_single( } // generate the basic pattern + //生成基本模式 coord_t x_max = bounding_box.max(0) + SCALED_EPSILON; Lines lines; for (coord_t x = bounding_box.min(0); x <= x_max; x += this->_line_spacing) @@ -49,6 +54,9 @@ void FillLine::_fill_surface_single( // however we use a larger offset to support expolygons with slightly skewed sides and // not perfectly straight //FIXME Vojtech: Update the intersecton function to work directly with lines. + //将路径剪切到稍大的expolygon上,这样即使expolygon有垂直边,也能保持第一条和最后一条路径,防止边缘线被剪切的最小偏移量为SCALED_EPSILON; + //然而,我们使用更大的偏移量来支持边略微倾斜且不完全笔直的expolygon + //FIXME Vojtech:更新交集函数以直接处理线条。 Polylines polylines_src; polylines_src.reserve(lines.size()); for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) { @@ -61,8 +69,10 @@ void FillLine::_fill_surface_single( Polylines polylines = intersection_pl(polylines_src, offset(expolygon, scale_(0.02))); // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines! + //FIXME Vojtech:这只适用于水平线,不适用于垂直线! const float INFILL_OVERLAP_OVER_SPACING = 0.3f; // How much to extend an infill path from expolygon outside? + //将填充路径从expolygon向外延伸多少? coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f)); for (Polylines::iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { Point *first_point = &it_polyline->points.front(); @@ -76,13 +86,14 @@ void FillLine::_fill_surface_single( size_t n_polylines_out_old = polylines_out.size(); // connect lines - if (! params.dont_connect() && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections + if (! params.dont_connect() && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections//防止对空集合调用left most_point() // offset the expolygon by max(min_spacing/2, extra) ExPolygon expolygon_off; { ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2); if (! expolygons_off.empty()) { // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island. + //展开多边形时,岛的数量只能缩小。因此,offset_ex应为一个输入岛生成一个扩展岛。 assert(expolygons_off.size() == 1); std::swap(expolygon_off, expolygons_off.front()); } @@ -98,6 +109,8 @@ void FillLine::_fill_surface_single( const Vector distance = last_point - first_point; // TODO: we should also check that both points are on a fill_boundary to avoid // connecting paths on the boundaries of internal regions + //TODO:我们还应该检查这两个点是否都在fill_boundary上,以避免 + //内部区域边界上的连接路径 if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && expolygon_off.contains(Line(last_point, first_point))) { // Append the polyline. @@ -106,14 +119,17 @@ void FillLine::_fill_surface_single( } } // The lines cannot be connected. + //线路无法连接。 polylines_out.emplace_back(std::move(polyline)); first = false; } } // paths must be rotated back + //路径必须向后旋转 for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_old; it != polylines_out.end(); ++ it) { // No need to translate, the absolute position is irrelevant. + //无需翻译,绝对位置无关紧要。 // it->translate(- direction.second(0), - direction.second(1)); it->rotate(direction.first); } diff --git a/libslic3r/GCode.cpp b/libslic3r/GCode.cpp index d9451c2..fc34046 100644 --- a/libslic3r/GCode.cpp +++ b/libslic3r/GCode.cpp @@ -1770,9 +1770,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Get optimal tool ordering to minimize tool switches of a multi-exruder print. // For a print by objects, find the 1st printing object. + //获得最佳的刀具订购,以最大限度地减少多喷嘴打印的刀具切换。 + //对于按对象打印,请找到第一个打印对象。 ToolOrdering tool_ordering; unsigned int initial_extruder_id = (unsigned int)-1; //BBS: first non-support filament extruder + //BBS:第一台无支撑长丝挤出机 unsigned int initial_non_support_extruder_id; unsigned int final_extruder_id = (unsigned int)-1; bool has_wipe_tower = false; @@ -1780,14 +1783,17 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato std::vector::const_iterator print_object_instance_sequential_active; if (print.config().print_sequence == PrintSequence::ByObject) { // Order object instances for sequential print. + //订购对象实例以进行顺序打印。 print_object_instances_ordering = sort_object_instances_by_model_order(print); // print_object_instances_ordering = sort_object_instances_by_max_z(print); // Find the 1st printing object, find its tool ordering and the initial extruder ID. + //找到第一个打印对象,找到其工具订购和初始挤出机ID。 print_object_instance_sequential_active = print_object_instances_ordering.begin(); for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) { tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id); if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast(-1)) { //BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament + //BBS:如果是多色的,初始挤出机是支撑长丝,试着找到非支撑长丝挤出机 initial_non_support_extruder_id = initial_extruder_id; if (tool_ordering.all_extruders().size() > 1 && print.config().filament_is_support.get_at(initial_extruder_id)) { bool has_non_support_filament = false; @@ -1798,6 +1804,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } } //BBS: find the non-support filament extruder of object + //BBS:查找非支撑长丝挤出机的对象 if (has_non_support_filament) { bool find_initial_non_support_filament = false; for (LayerTools layer_tools : tool_ordering.layer_tools()) { @@ -1822,22 +1829,29 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } if (initial_extruder_id == static_cast(-1)) // No object to print was found, cancel the G-code export. + //未找到要打印的对象,请取消G代码导出。 throw Slic3r::SlicingError(_(L("No object can be printed. Maybe too small"))); // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. // Use the extruder IDs collected from Regions. + //我们不允许在顺序模式下按Model::custom_gcode_per_print_z切换每层挤出机。 + //使用从区域收集的挤出机ID。 this->set_extruders(print.extruders()); has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); } else { // Find tool ordering for all the objects at once, and the initial extruder ID. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. + //一次查找所有对象的工具顺序和初始挤出机ID。 + //如果擦拭塔的Print类已经预先计算了工具订购量,请重新使用。 tool_ordering = print.tool_ordering(); tool_ordering.assign_custom_gcodes(print); if (tool_ordering.all_extruders().empty()) // No object to print was found, cancel the G-code export. + //未找到要打印的对象,请取消G代码导出。 throw Slic3r::SlicingError(_(L("No object can be printed. Maybe too small"))); has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); // BBS: priming logic is removed, so 1st layer tool_ordering also respect the object tool sequence + //BBS:删除了启动逻辑,因此第一层工具排序也尊重对象工具序列 #if 0 initial_extruder_id = (has_wipe_tower && !print.config().single_extruder_multi_material_priming) ? // The priming towers will be skipped. @@ -1848,6 +1862,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato initial_extruder_id = tool_ordering.first_extruder(); #endif //BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament + //BBS:如果是多色的,初始挤出机是支撑长丝,试着找到非支撑长丝挤出机 if (initial_extruder_id != static_cast(-1)) { initial_non_support_extruder_id = initial_extruder_id; if (tool_ordering.all_extruders().size() > 1 && print.config().filament_is_support.get_at(initial_extruder_id)) { @@ -1859,6 +1874,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } } //BBS: find the non-support filament extruder of object + //BBS:查找非支撑长丝挤出机的对象 if (has_non_support_filament){ bool find_initial_non_support_filament = false; for (LayerTools layer_tools : tool_ordering.layer_tools()) { @@ -1881,8 +1897,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. // Therefore initialize the printing extruders from there. + //在非顺序打印中,打印挤出机可能已被存储在Model::custom_gcode_per_print_z中的挤出机开关修改。 + //因此,从那里初始化打印挤出机。 this->set_extruders(tool_ordering.all_extruders()); // Order object instances using a nearest neighbor search. + //使用最近邻搜索对对象实例进行排序。 print_object_instances_ordering = chain_print_object_instances(print); } if (initial_extruder_id == (unsigned int)-1) { @@ -1896,6 +1915,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_cooling_buffer->set_current_extruder(initial_extruder_id); // Emit machine envelope limits for the Marlin firmware. + //发射Marlin固件的机器包络限制。 this->print_machine_envelope(file, print); // Disable fan. @@ -1906,6 +1926,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // Let the start-up script prime the 1st printing tool. + //让启动脚本启动第一个打印工具。 m_placeholder_parser.set("initial_tool", initial_extruder_id); m_placeholder_parser.set("initial_extruder", initial_extruder_id); //BBS @@ -1913,16 +1934,20 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_placeholder_parser.set("initial_no_support_extruder", initial_non_support_extruder_id); m_placeholder_parser.set("current_extruder", initial_extruder_id); //set the key for compatibilty + //设置兼容性密钥 m_placeholder_parser.set("retraction_distance_when_cut", m_config.retraction_distances_when_cut.get_at(initial_extruder_id)); m_placeholder_parser.set("long_retraction_when_cut", m_config.long_retractions_when_cut.get_at(initial_extruder_id)); m_placeholder_parser.set("retraction_distances_when_cut", new ConfigOptionFloats(m_config.retraction_distances_when_cut)); m_placeholder_parser.set("long_retractions_when_cut",new ConfigOptionBools(m_config.long_retractions_when_cut)); //Set variable for total layer count so it can be used in custom gcode. + //设置总层数变量,以便在自定义gcode中使用。 m_placeholder_parser.set("total_layer_count", m_layer_count); // Useful for sequential prints. + //适用于连续打印。 m_placeholder_parser.set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. + //在没有提供擦拭塔的情况下,开始/结束G代码可以进行底漆和最终灯丝拉拔。 m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); //m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). @@ -1939,6 +1964,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // It does NOT encompass user extrusions generated by custom G-code, // therefore it does NOT encompass the initial purge line. // It does NOT encompass MMU/MMU2 starting (wipe) areas. + //第一层挤压件的凸面外壳,用于床层平整和放置初始吹扫管线。 + //它包括物体拉伸、支撑拉伸、裙子、帽沿、擦拭塔。 + //它不包括由自定义G代码生成的用户拉伸, + //因此,它不包括初始净化管线。 + //它不包括MMU/MMU2启动(擦除)区域。 auto pts = std::make_unique(); pts->values.reserve(print.first_layer_convex_hull().size()); for (const Point &pt : print.first_layer_convex_hull().points) @@ -1957,6 +1987,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { // BBS:deal with head wrap detect // use first layer convex_hull union with each object's bbox to check whether in head detect zone + //BBS:处理头部包裹检测 + //使用第一层凸包联合每个对象的bbox来检查是否在头部检测区域 Polygons object_projections; for (auto& obj : print.objects()) { for (auto& instance : obj->instances()) { @@ -1983,7 +2015,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // get center without wipe tower - BoundingBoxf bbox_wo_wt;// bounding box without wipe tower + //无需擦拭塔即可获得中心 + BoundingBoxf bbox_wo_wt;// bounding box without wipe tower//无擦拭塔的边界框 for (auto& objPtr : print.objects()) { BBoxData data; bbox_wo_wt.merge(unscaled(objPtr->get_first_layer_bbox(data.area, data.layer_height, data.name))); @@ -2024,6 +2057,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato during_print_exhaust_fan_speed_num.emplace_back((int)(item / 100.0 * 255)); m_placeholder_parser.set("during_print_exhaust_fan_speed_num",new ConfigOptionInts(during_print_exhaust_fan_speed_num)); //BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting + //BBS:计算外壁的体积速度。忽略预对象设置和多灯丝,只使用默认设置 { float filament_max_volumetric_speed = m_config.option("filament_max_volumetric_speed")->get_at(initial_non_support_extruder_id); float outer_wall_line_width = print.default_region_config().outer_wall_line_width.value; @@ -2046,22 +2080,28 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato std::string machine_start_gcode = this->placeholder_parser_process("machine_start_gcode", print.config().machine_start_gcode.value, initial_extruder_id); if (print.config().gcode_flavor != gcfKlipper) { // Set bed temperature if the start G-code does not contain any bed temp control G-codes. + //如果启动G代码不包含任何床温控制G代码,则设置床温。 this->_print_first_layer_bed_temperature(file, print, machine_start_gcode, initial_extruder_id, true); // Set extruder(s) temperature before and after start G-code. + //在启动G-代码之前和之后设置挤出机温度。 this->_print_first_layer_extruder_temperatures(file, print, machine_start_gcode, initial_extruder_id, false); } // BBS: chamber temp control for 3rd printers + //BBS:第三台打印机的腔室温度控制 if (!is_BBL_Printer() && print.config().support_chamber_temp_control.value && max_chamber_temp > 0 ){ file.write(m_writer.set_chamber_temperature(max_chamber_temp,true)); } // adds tag for processor + //为处理器添加标签 file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); // Write the custom start G-code + //编写自定义启动G代码 file.writeln(machine_start_gcode); //BBS: gcode writer doesn't know where the real position of extruder is after inserting custom gcode + //BBS:插入自定义gcode后,gcode编写者不知道挤出机的实际位置在哪里 m_writer.set_current_position_clear(false); m_start_gcode_filament = GCodeProcessor::get_gcode_last_filament(machine_start_gcode); @@ -2084,17 +2124,21 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.throw_if_canceled(); // Set other general things. + //设置其他一般事项。 file.write(this->preamble()); // Calculate wiping points if needed + //如果需要,计算擦拭点 DoExport::init_ooze_prevention(print, m_ooze_prevention); print.throw_if_canceled(); // Collect custom seam data from all objects. + //从所有对象收集自定义接缝数据。 std::function throw_if_canceled_func = [&print]() { print.throw_if_canceled(); }; m_seam_placer.init(print, throw_if_canceled_func); // BBS: get path for change filament + //BBS:获取更换灯丝的路径 if (m_writer.multiple_extruders) { std::vector points = get_path_of_change_filament(print); if (points.size() == 3) { @@ -2105,6 +2149,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // BBS: priming logic is removed, always set first extruer here. + //BBS:启动逻辑已删除,始终在此处设置第一台挤出机。 //if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) { // Set initial extruder only after custom start G-code. @@ -2112,6 +2157,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(this->set_extruder(initial_extruder_id, 0.)); } // BBS: set that indicates objs with brim + //BBS:用边缘表示对象的集合 for (auto iter = print.m_brimMap.begin(); iter != print.m_brimMap.end(); ++iter) { if (!iter->second.empty()) this->m_objsWithBrim.insert(iter->first); @@ -2148,10 +2194,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } else { // BBS: open spaghetti detector + //BBS:开放式意大利面条检测器 // if (print.config().spaghetti_detector.value) if (print.is_BBL_Printer()) file.write("M981 S1 P20000 ;open spaghetti detector\n"); // Do all objects for each layer. + //为每个图层执行所有对象。 if (print.config().print_sequence == PrintSequence::ByObject && !has_wipe_tower) { size_t finished_objects = 0; const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object; @@ -2171,13 +2219,17 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->set_origin(unscale((*print_object_instance_sequential_active)->shift)); // BBS: prime extruder if extruder change happens before this object instance + //BBS:如果挤出机在此对象实例之前发生更改,则启动挤出机 bool prime_extruder = false; if (finished_objects > 0) { // Move to the origin position for the copy we're going to print. // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. - m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer + //移动到我们要打印的副本的原点位置。 + //这发生在Z再次下降到层0之前,所以希望不会发生碰撞。 + m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer//我们不会通过CoolingBuffer过滤这些动作 m_avoid_crossing_perimeters.use_external_mp_once(); // BBS. change tool before moving to origin point. + //BBS。在移动到原点之前更换工具。 if (m_writer.need_toolchange(initial_extruder_id)) { const PrintObjectConfig &object_config = object.config(); coordf_t initial_layer_print_height = print.config().initial_layer_print_height.value; @@ -2190,19 +2242,23 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); m_enable_cooling_markers = true; // Disable motion planner when traveling to first object point. + //在移动到第一个对象点时禁用运动规划器。 m_avoid_crossing_perimeters.disable_once(); // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. + //如果我们正在打印一个对象的底层,并且已经完成了另一个,请设置第一层温度。这发生在Z移动被触发之前,因此机器有更多的时间达到这样的温度。 m_placeholder_parser.set("current_object_idx", int(finished_objects)); std::string printing_by_object_gcode = this->placeholder_parser_process("printing_by_object_gcode", print.config().printing_by_object_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. + //设置第一层床和挤出机的温度,不要等到它达到温度。 this->_print_first_layer_bed_temperature(file, print, printing_by_object_gcode, initial_extruder_id, false); this->_print_first_layer_extruder_temperatures(file, print, printing_by_object_gcode, initial_extruder_id, false); file.writeln(printing_by_object_gcode); } // Reset the cooling buffer internal state (the current position, feed rate, accelerations). + //重置冷却缓冲器内部状态(当前位置、进给速度、加速度)。 m_cooling_buffer->reset(this->writer().get_position()); m_cooling_buffer->set_current_extruder(initial_extruder_id); // Process all layers of a single object instance (sequential mode) with a parallel pipeline: diff --git a/libslic3r/GCodeWriter.cpp b/libslic3r/GCodeWriter.cpp index 8892462..724cd25 100644 --- a/libslic3r/GCodeWriter.cpp +++ b/libslic3r/GCodeWriter.cpp @@ -435,7 +435,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co } else if (m_to_lift_type == LiftType::NormalLift) { slop_move = _travel_to_z(target.z(), "normal lift Z"); - slop_move = _travel_to_z_test(target.z(), "normal lift Z"); + //slop_move = _travel_to_z_test(target.z(), "normal lift Z"); } } @@ -444,11 +444,11 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co GCodeG1Formatter w0; if (this->is_current_position_clear()) { w0.emit_xyz(target); - //auto dE = m_extruder->E(); + /* //auto dE = m_extruder->E(); auto dE = 1; if (dE > 0) { w0.emit_e(dE); - } + }*/ w0.emit_f(this->config.travel_speed.value * 60.0); w0.emit_comment(GCodeWriter::full_gcode_comment, comment); xy_z_move = w0.string(); @@ -474,8 +474,8 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co m_lifted = 0.; //BBS this->set_current_position_clear(true); - //return this->travel_to_xy(to_2d(point)); - return this->travel_to_xy_test(to_2d(point)); + return this->travel_to_xy(to_2d(point)); + //return this->travel_to_xy_test(to_2d(point)); } else { /* In all the other cases, we perform an actual XYZ move and cancel diff --git a/libslic3r/Preset.cpp b/libslic3r/Preset.cpp index 6130c30..f24cec1 100644 --- a/libslic3r/Preset.cpp +++ b/libslic3r/Preset.cpp @@ -805,7 +805,7 @@ static std::vector s_Preset_print_options { "minimum_sparse_infill_area", "reduce_infill_retraction", "ironing_pattern", "ironing_type", "ironing_flow", "ironing_speed", "ironing_spacing","ironing_direction", "max_travel_detour_distance", - "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance","fibre_feed_rate", + "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance",//"fibre_feed_rate", #ifdef HAS_PRESSURE_EQUALIZER "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", #endif /* HAS_PRESSURE_EQUALIZER */ @@ -996,16 +996,16 @@ static std::vector s_Preset_sla_printer_options { "min_initial_exposure_time", "max_initial_exposure_time", "inherits" }; -static std::vector s_Preset_config_options{ - "initial_layer_print_height","initial_layer_line_width","outer_wall_line_width","inner_wall_line_width","top_surface_line_width","sparse_infill_line_width","support_line_width","seam_gap","seam_slope_conditional","scarf_angle_threshold","seam_slope_start_height", - "seam_slope_entire_loop","seam_slope_min_length","seam_slope_steps","seam_slope_inner_walls","wipe_speed","resolution","enable_arc_fitting","ironing_pattern","ironing_speed","ironing_flow","ironing_spacing","ironing_direction","wall_generator","wall_transition_angle", - "wall_transition_filter_deviation","wall_transition_length","wall_distribution_count","min_bead_width","min_feature_size","wall_sequence","top_one_wall_type","top_area_threshold","only_one_wall_first_layer","detect_overhang_wall","max_travel_detour_distance","support_base_pattern_spacing", - "support_interface_top_layers","support_interface_bottom_layers","support_bottom_interface_spacing","top_surface_pattern","bottom_surface_pattern","internal_solid_infill_pattern","sparse_infill_pattern","filter_out_gap_fill","bridge_angle","minimum_sparse_infill_area","infill_combination", - "default_print_speed","initial_layer_speed","initial_layer_infill_speed","outer_wall_speed","inner_wall_speed","sparse_infill_speed","internal_solid_infill_speed","top_surface_speed","bridge_speed","gap_infill_speed","support_speed","support_interface_speed","travel_speed", - "default_acceleration","accel_to_decel_enable","accel_to_decel_factor","support_type","support_style","support_threshold_angle","support_on_build_plate_only","support_critical_regions_only","support_remove_small_overhang","raft_layers","raft_contact_distance","raft_first_layer_density", - "support_filament","support_interface_filament","support_interface_not_for_body","raft_first_layer_expansion","tree_support_wall_count","support_base_pattern","support_interface_pattern","support_object_xy_distance","bridge_no_support","max_bridge_length","independent_support_layer_height", - "tree_support_branch_distance","tree_support_branch_diameter","tree_support_branch_angle","slicing_mode","fibre_feed_rate","print_sequence","spiral_mode","spiral_mode_smooth","spiral_mode_max_xy_smoothing","timelapse_type","fuzzy_skin","fuzzy_skin_point_distance","fuzzy_skin_thickness" -}; +//static std::vector s_Preset_config_options{ +// "initial_layer_print_height","initial_layer_line_width","outer_wall_line_width","inner_wall_line_width","top_surface_line_width","sparse_infill_line_width","support_line_width","seam_gap","seam_slope_conditional","scarf_angle_threshold","seam_slope_start_height", +// "seam_slope_entire_loop","seam_slope_min_length","seam_slope_steps","seam_slope_inner_walls","wipe_speed","resolution","enable_arc_fitting","ironing_pattern","ironing_speed","ironing_flow","ironing_spacing","ironing_direction","wall_generator","wall_transition_angle", +// "wall_transition_filter_deviation","wall_transition_length","wall_distribution_count","min_bead_width","min_feature_size","wall_sequence","top_one_wall_type","top_area_threshold","only_one_wall_first_layer","detect_overhang_wall","max_travel_detour_distance","support_base_pattern_spacing", +// "support_interface_top_layers","support_interface_bottom_layers","support_bottom_interface_spacing","top_surface_pattern","bottom_surface_pattern","internal_solid_infill_pattern","sparse_infill_pattern","filter_out_gap_fill","bridge_angle","minimum_sparse_infill_area","infill_combination", +// "default_print_speed","initial_layer_speed","initial_layer_infill_speed","outer_wall_speed","inner_wall_speed","sparse_infill_speed","internal_solid_infill_speed","top_surface_speed","bridge_speed","gap_infill_speed","support_speed","support_interface_speed","travel_speed", +// "default_acceleration","accel_to_decel_enable","accel_to_decel_factor","support_type","support_style","support_threshold_angle","support_on_build_plate_only","support_critical_regions_only","support_remove_small_overhang","raft_layers","raft_contact_distance","raft_first_layer_density", +// "support_filament","support_interface_filament","support_interface_not_for_body","raft_first_layer_expansion","tree_support_wall_count","support_base_pattern","support_interface_pattern","support_object_xy_distance","bridge_no_support","max_bridge_length","independent_support_layer_height", +// "tree_support_branch_distance","tree_support_branch_diameter","tree_support_branch_angle","slicing_mode","fibre_feed_rate","print_sequence","spiral_mode","spiral_mode_smooth","spiral_mode_max_xy_smoothing","timelapse_type","fuzzy_skin","fuzzy_skin_point_distance","fuzzy_skin_thickness" +//}; const std::vector& Preset::print_options() { return s_Preset_print_options; } const std::vector& Preset::filament_options() { return s_Preset_filament_options; } @@ -1016,7 +1016,7 @@ const std::vector& Preset::nozzle_options() { return print_ const std::vector& Preset::sla_print_options() { return s_Preset_sla_print_options; } const std::vector& Preset::sla_material_options() { return s_Preset_sla_material_options; } const std::vector& Preset::sla_printer_options() { return s_Preset_sla_printer_options; } -const std::vector& Preset::config_options() { return s_Preset_config_options; } +const std::vector& Preset::config_options() { return s_Preset_print_options; } const std::vector& Preset::printer_options() { diff --git a/libslic3r/PresetBundle.cpp b/libslic3r/PresetBundle.cpp index 7f755bb..b0d09f5 100644 --- a/libslic3r/PresetBundle.cpp +++ b/libslic3r/PresetBundle.cpp @@ -2036,6 +2036,7 @@ DynamicPrintConfig PresetBundle::full_fff_config() const // Add the default filament preset to have the "filament_preset_id" defined. out.apply(this->filaments.default_preset().config); out.apply(this->printers.get_edited_preset().config); + out.apply(this->configs.get_edited_preset().config); out.apply(this->project_config); // BBS diff --git a/libslic3r/Print.cpp b/libslic3r/Print.cpp index 02f4abc..bcdaf69 100644 --- a/libslic3r/Print.cpp +++ b/libslic3r/Print.cpp @@ -1553,6 +1553,7 @@ std::map getObjectExtruderMap(const Print& print) { } // Slicing process, running at a background thread. +//切片过程,在后台线程上运行。 void Print::process(std::unordered_map* slice_time, bool use_cache) { long long start_time = 0, end_time = 0; @@ -1567,6 +1568,7 @@ void Print::process(std::unordered_map* slice_time, bool name_tbb_thread_pool_threads_set_locale(); //compute the PrintObject with the same geometries + //使用相同的几何图形计算PrintObject BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter, use_cache=%2%, object size=%3%")%this%use_cache%m_objects.size(); if (m_objects.empty()) return; @@ -1575,6 +1577,7 @@ void Print::process(std::unordered_map* slice_time, bool obj->clear_shared_object(); //add the print_object share check logic + //添加打印对象共享检查逻辑 auto is_print_object_the_same = [this](const PrintObject* object1, const PrintObject* object2) -> bool{ if (object1->trafo().matrix() != object2->trafo().matrix()) return false; @@ -1658,6 +1661,8 @@ void Print::process(std::unordered_map* slice_time, bool BOOST_LOG_TRIVIAL(warning) << boost::format("Also can not find the shared object, identify_id %1%, maybe shared object is skipped")%obj->model_object()->instances[0]->loaded_id; //throw Slic3r::SlicingError("Can not find the cached data."); //don't report errot, set use_cache to false, and reslice these objects + //throw Slic3r::SlicingError(“找不到缓存的数据。”); + //不报告错误,将use_cache设置为false,并重新切片这些对象 need_slicing_objects.insert(obj); re_slicing_objects.insert(obj); //use_cache = false; @@ -1799,6 +1804,7 @@ void Print::process(std::unordered_map* slice_time, bool this->_make_wipe_tower(); } else if (this->config().print_sequence != PrintSequence::ByObject) { // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches. + //初始化刀具订购,以便G代码预览滑块可以使用它来计划刀具更换和灯丝切换。 m_tool_ordering = ToolOrdering(*this, -1, false); if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1)) throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings."); @@ -1820,10 +1826,12 @@ void Print::process(std::unordered_map* slice_time, bool if (this->has_skirt() && draft_shield) { // In case that draft shield is active, generate skirt first so brim // can be trimmed to make room for it. + //如果防风罩处于活动状态,请先制作裙子,这样帽沿就可以修剪,为它腾出空间。 _make_skirt(); } //BBS: get the objects' indices when GCodes are generated + //BBS:生成GCode时获取对象的索引 ToolOrdering tool_ordering; unsigned int initial_extruder_id = (unsigned int)-1; unsigned int final_extruder_id = (unsigned int)-1; @@ -1834,6 +1842,7 @@ void Print::process(std::unordered_map* slice_time, bool std::vector printExtruders; if (this->config().print_sequence == PrintSequence::ByObject) { // Order object instances for sequential print. + //订购对象实例以进行顺序打印。 print_object_instances_ordering = sort_object_instances_by_model_order(*this); // print_object_instances_ordering = sort_object_instances_by_max_z(print); print_object_instance_sequential_active = print_object_instances_ordering.begin(); @@ -1873,9 +1882,10 @@ void Print::process(std::unordered_map* slice_time, bool objPrintVec.push_back(std::make_pair(print_object_ID, objectExtruderMap.at(print_object_ID))); } // BBS: m_brimMap and m_supportBrimMap are used instead of m_brim to generate brim of objs and supports seperately + //BBS:使用m_brimMap和m_supportBrimMap代替m_period来生成对象边缘,并分别支持 m_brimMap.clear(); m_supportBrimMap.clear(); - m_first_layer_convex_hull.points.clear(); // BBS: plate offset is contained in this convexhull + m_first_layer_convex_hull.points.clear(); // BBS: plate offset is contained in this convexhull//BBS:板材偏移量包含在这个凸块中 if (this->has_brim()) { Polygons islands_area; make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap, @@ -1890,6 +1900,8 @@ void Print::process(std::unordered_map* slice_time, bool if (has_skirt() && ! draft_shield) { // In case that draft shield is NOT active, generate skirt now. // It will be placed around the brim, so brim has to be ready. + //如果导流罩未激活,请立即生成裙板。 + //它将被放置在帽沿周围,所以帽沿必须准备好。 assert(m_skirt.empty()); _make_skirt(); } @@ -1927,6 +1939,7 @@ void Print::process(std::unordered_map* slice_time, bool } } // TODO adaptive layer height won't work with conflict checker because m_fake_wipe_tower's path is generated using fixed layer height + //TODO自适应层高度不适用于冲突检查器,因为m_fake_wipe_tower的路径是使用固定层高度生成的 if(!m_no_check && !has_adaptive_layer_height) { using Clock = std::chrono::high_resolution_clock; @@ -1954,14 +1967,20 @@ void Print::process(std::unordered_map* slice_time, bool // The export_gcode may die for various reasons (fails to process filename_format, // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. +//G代码导出进程,在后台线程中运行。 +//export_gode可能会因各种原因而死亡(无法处理filename_format、在G-代码中写入错误、无法执行后处理脚本)。 +//由呼叫者显示错误消息。 std::string Print::export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) { // output everything to a G-code file // The following call may die if the filename_format template substitution fails. + //将所有内容输出到G代码文件 + //如果filename_format模板替换失败,则以下调用可能会终止。 std::string path = this->output_filepath(path_template); std::string message; if (!path.empty() && result == nullptr) { // Only show the path if preview_data is not set -> running from command line. + //仅在未设置preview_data时显示路径->从命令行运行。 message = L("Exporting G-code"); message += " to "; message += path; @@ -1970,8 +1989,10 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor this->set_status(80, message); // The following line may die for multiple reasons. + //以下行可能因多种原因而终止。 GCode gcode; //BBS: compute plate offset for gcode-generator + //BBS:计算gcode生成器的板偏移 const Vec3d origin = this->get_plate_origin(); gcode.set_gcode_offset(origin(0), origin(1)); gcode.do_export(this, path.c_str(), result, thumbnail_cb); diff --git a/libslic3r/PrintConfig.cpp b/libslic3r/PrintConfig.cpp index 395e1cf..3379ad3 100644 --- a/libslic3r/PrintConfig.cpp +++ b/libslic3r/PrintConfig.cpp @@ -1167,7 +1167,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); //xiamian+ - //def->enum_values.push_back("fiberspiral"); + def->enum_values.push_back("fiberspiral"); def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Rectilinear")); @@ -1179,7 +1179,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); //xiamian+ - //def->enum_labels.push_back(L("Fiber Spiral")); + def->enum_labels.push_back(L("Fiber Spiral")); def->set_default_value(new ConfigOptionEnum(ipRectilinear)); def = this->add("bottom_surface_pattern", coEnum); @@ -1625,7 +1625,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("lightning"); def->enum_values.push_back("crosshatch"); //xiamian+ - //def->enum_values.push_back("fiberspiral"); + def->enum_values.push_back("fiberspiral"); def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Grid")); @@ -1646,7 +1646,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Lightning")); def->enum_labels.push_back(L("Cross Hatch")); //xiamian+ - //def->enum_labels.push_back(L("Fiber Spiral")); + def->enum_labels.push_back(L("Fiber Spiral")); def->set_default_value(new ConfigOptionEnum(ipGrid)); def = this->add("top_surface_acceleration", coFloat); @@ -1867,7 +1867,7 @@ void PrintConfigDef::init_fff_params() def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(0.3)); - def = this->add("fibre_feed_rate", coFloats); + /*def = this->add("fibre_feed_rate", coFloats); def->label = L("Fibre feed rate"); def->category = L("Others"); //def->tooltip = L("The width within which to jitter. It's adversed to be below outer wall line width"); @@ -1876,6 +1876,7 @@ void PrintConfigDef::init_fff_params() def->max = 2; def->mode = comSimple; def->set_default_value(new ConfigOptionFloats{ 1. }); + */ def = this->add("fuzzy_skin_point_distance", coFloat); def->label = L("Fuzzy skin point distance"); diff --git a/libslic3r/PrintConfig.hpp b/libslic3r/PrintConfig.hpp index 132860b..0c7ee4e 100644 --- a/libslic3r/PrintConfig.hpp +++ b/libslic3r/PrintConfig.hpp @@ -895,7 +895,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionString, machine_end_gcode)) ((ConfigOptionStrings, filament_end_gcode)) ((ConfigOptionFloats, filament_flow_ratio)) - ((ConfigOptionFloats, fibre_feed_rate)) + //((ConfigOptionFloats, fibre_feed_rate)) ((ConfigOptionBools, enable_pressure_advance)) ((ConfigOptionFloats, pressure_advance)) ((ConfigOptionFloats, filament_diameter)) diff --git a/libslic3r/PrintObject.cpp b/libslic3r/PrintObject.cpp index 69e124e..83430d4 100644 --- a/libslic3r/PrintObject.cpp +++ b/libslic3r/PrintObject.cpp @@ -132,9 +132,13 @@ std::vector> PrintObject::all_regions( // 1) Merges typed region slices into stInternal type. // 2) Increases an "extra perimeters" counter at region slices where needed. // 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). +//1)将类型化的区域切片合并为stInternal类型。 +//2)在需要的区域切片处增加“额外周长”计数器。 +//3)生成周界、间隙填充和填充区域(stInternal类型的填充区域)。 void PrintObject::make_perimeters() { // prerequisites + //先决条件 this->slice(); if (! this->set_started(posPerimeters)) @@ -144,6 +148,7 @@ void PrintObject::make_perimeters() BOOST_LOG_TRIVIAL(info) << "Generating walls..." << log_memory_info(); // Revert the typed slices into untyped slices. + //将类型化切片还原为非类型化切片。 if (m_typed_slices) { for (Layer *layer : m_layers) { layer->restore_untyped_slices(); @@ -159,6 +164,8 @@ void PrintObject::make_perimeters() // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing // hollow objects + //将每一层与下面的一层进行比较,并标记需要额外内周的切片,如圆顶对象的顶部- + //该算法确保至少有一个周长是重叠的,但如果填充密度为零,我们不会生成任何额外的周长,因为它们将漂浮在对象内部-仅填充,其中需要填充应该是打印空心对象的首选方法 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); //BBS: remove extra_perimeters, always false @@ -175,6 +182,7 @@ void PrintObject::make_perimeters() const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->get_region(region_id); const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces); // Filter upper layer polygons in intersection_ppl by their bounding boxes? + //通过边界框过滤intersection_ppl中的上层多边形? // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; const double total_loop_length = total_length(upper_layerm_polygons); const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); @@ -185,9 +193,11 @@ void PrintObject::make_perimeters() for (Surface &slice : layerm.slices.surfaces) { for (;;) { // compute the total thickness of perimeters + //计算周界的总厚度 const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 + (region.config().wall_loops-1 + slice.extra_perimeters) * perimeter_spacing; // define a critical area where we don't want the upper slice to fall into + //定义一个关键区域,我们不希望上部切片落入其中 // (it should either lay over our perimeters or outside this area) const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); const Polygons critical_area = diff( @@ -195,8 +205,10 @@ void PrintObject::make_perimeters() offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth)) ); // check whether a portion of the upper slices falls inside the critical area + //检查上部切片的一部分是否落在临界区域内 const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area); // only add an additional loop if at least 30% of the slice loop would benefit from it + //仅当切片循环的至少30%将从中受益时,才添加额外的循环 if (total_length(intersection) <= total_loop_length*0.3) break; /* @@ -262,10 +274,13 @@ void PrintObject::prepare_infill() // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. + //这将为$layer->切片分配一个类型(顶部/底部/内部)。 + //然后,通过用前一个$layer->fill_surfaces的累积面积裁剪$layer-->fill_surface,将$layer->切片的分类转移到$layer-->fill_surposes上。 this->detect_surfaces_type(); m_print->throw_if_canceled(); // Also tiny stInternal surfaces are turned to stInternalSolid. + //此外,微小的stInternal表面也会变成stInternalSolid。 BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) for (auto *region : layer->m_regions) { @@ -274,6 +289,7 @@ void PrintObject::prepare_infill() } // Add solid fills to ensure the shell vertical thickness. + //添加实心填充物以确保壳体的垂直厚度。 this->discover_vertical_shells(); m_print->throw_if_canceled(); @@ -286,6 +302,13 @@ void PrintObject::prepare_infill() // 3) Clip the internal surfaces by the grown top/bottom surfaces. // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. + //这将检测桥接和反向桥接,并重新排列顶部/底部/内部表面 + //它产生了扩大的重叠桥接区域。 + //1)stBottomBridge/stBottom填充物增长了3mm,并被总填充面积所限。检测到桥梁。这些区域可能会重叠。 + //2)stTop生长3mm,并被生长的底部区域夹住。这些区域可能会重叠。 + //3)用生长的顶面/底面夹住内表面。 + //4)合并具有相同样式的曲面。这将主要消除重叠。 + //FIXME这不太可能合并由具有不同颜色但属性相同的材质支持的曲面。 this->process_external_surfaces(); m_print->throw_if_canceled(); @@ -307,6 +330,10 @@ void PrintObject::prepare_infill() // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces // to close these surfaces reliably. //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters? + //检测哪些填充表面靠近外层。 + //它们将分为内部和内部固体表面。 + //目的是添加可配置数量的实体层来支撑顶部表面,并在底部/底部桥表面上方添加可配置的实体层数量,以可靠地闭合这些表面。 + //FIXME Vojtech:这是在倾斜周界下方添加支撑填充物的好地方吗? this->discover_horizontal_shells(); m_print->throw_if_canceled(); @@ -327,6 +354,11 @@ void PrintObject::prepare_infill() //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. + //仅当config->fill_Only_where_needed时才激活。此步骤修剪稀疏填充,使其充当内部支撑。它保持所有其他填充类型的完整性。 + //这里的内表面和周界必须由稀疏填充物支撑。 + //FIXME曲面由稀疏填充物支持,但稀疏填充物的大小仅与要支持的区域一样大。 + //稀疏填充可能无法正确锚定,因此无法按预期工作。 + //此外,人们还希望周边由完整的填充物支撑。 this->clip_fill_surfaces(); m_print->throw_if_canceled(); @@ -342,10 +374,12 @@ void PrintObject::prepare_infill() // the following step needs to be done before combination because it may need // to remove only half of the combined infill + //在组合之前需要完成以下步骤,因为它可能只需要删除组合填充的一半 this->bridge_over_infill(); m_print->throw_if_canceled(); // combine fill surfaces to honor the "infill every N layers" option + //组合填充曲面以实现“每N层填充”选项 this->combine_infill(); m_print->throw_if_canceled(); diff --git a/libslic3r/PrintObjectSlice.cpp b/libslic3r/PrintObjectSlice.cpp index d4d2941..ac0215b 100644 --- a/libslic3r/PrintObjectSlice.cpp +++ b/libslic3r/PrintObjectSlice.cpp @@ -116,6 +116,10 @@ static inline bool model_volume_needs_slicing(const ModelVolume &mv) // Apply closing radius. // Apply positive XY compensation to ModelVolumeType::MODEL_PART and ModelVolumeType::PARAMETER_MODIFIER, not to ModelVolumeType::NEGATIVE_VOLUME. // Apply contour simplification. +//切片可打印卷、负卷和修改器卷,按ModelVolume::id()排序。 +//应用闭合半径。 +//将正XY补偿应用于ModelVolumeType::MODEL_PART和ModelVolumeType::PARAMETERMODIFIER,而不是应用于ModelVolumeType::NEGATIVE_VOLUME。 +//应用轮廓简化。 static std::vector slice_volumes_inner( const PrintConfig &print_config, const PrintObjectConfig &print_object_config, @@ -140,6 +144,8 @@ static std::vector slice_volumes_inner( params_base.trafo = object_trafo; //BBS: 0.0025mm is safe enough to simplify the data to speed slicing up for high-resolution model. //Also has on influence on arc fitting which has default resolution 0.0125mm. + //BBS:0.0025mm足够安全,可以简化数据,加快高分辨率模型的切片速度。 + //对默认分辨率为0.0125mm的圆弧拟合也没有影响。 params_base.resolution = print_config.resolution <= 0.001 ? 0.0f : 0.0025; switch (print_object_config.slicing_mode.value) { case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break; @@ -154,6 +160,8 @@ static std::vector slice_volumes_inner( const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); }); // BBS: don't do size compensation when slice volume. // Will handle contour and hole size compensation seperately later. + //BBS:切片时不要做尺寸补偿。 + //稍后将分别处理轮廓和孔尺寸补偿。 //const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_contour_compensation.value)); const auto extra_offset = 0.f; @@ -170,6 +178,8 @@ static std::vector slice_volumes_inner( params.mode = MeshSlicingParams::SlicingMode::PositiveLargestContour; // Slice the bottom layers with SlicingMode::Regular. // This needs to be in sync with LayerRegion::make_perimeters() spiral_mode! + //使用SlicingMode::Regular对底层进行切片。 + //这需要与LayerRegion::make_perimeters()spiral_mode同步! const PrintRegionConfig ®ion_config = it->region->config(); params.slicing_mode_normal_below_layer = size_t(region_config.bottom_shell_layers.value); for (; params.slicing_mode_normal_below_layer < zs.size() && zs[params.slicing_mode_normal_below_layer] < region_config.bottom_shell_thickness - EPSILON; @@ -244,6 +254,8 @@ static std::vector> slices_to_regions( std::vector &&volume_slices, // If clipping is disabled, then ExPolygons produced by different volumes will never be merged, thus they will be allowed to overlap. // It is up to the model designer to handle these overlaps. + //如果禁用剪裁,则由不同体积生成的ExPolygon将永远不会合并,因此它们将被允许重叠。 + //由模型设计者来处理这些重叠。 const bool clip_multipart_objects, const std::function &throw_on_cancel_callback) { @@ -252,6 +264,7 @@ static std::vector> slices_to_regions( std::vector> slices_by_region(print_object_regions.all_regions.size(), std::vector(zs.size(), ExPolygons())); // First shuffle slices into regions if there is no overlap with another region possible, collect zs of the complex cases. + //首先,若并没有可能和另一个区域重叠,则将切片洗牌到各个区域,收集复杂案例的zs。 std::vector> zs_complex; { size_t z_idx = 0; @@ -280,6 +293,7 @@ static std::vector> slices_to_regions( idx_first_printable_region = idx_region; else if (idx_first_printable_region != -1) { // Test for overlap with some other region. + //测试是否与其他区域重叠。 for (int idx_region2 = idx_first_printable_region; idx_region2 < idx_region; ++ idx_region2) { const PrintObjectRegions::VolumeRegion ®ion2 = layer_range.volume_regions[idx_region2]; if (region2.bbox->min().z() <= z && region2.bbox->max().z() >= z && overlap_in_xy(*region.bbox, *region2.bbox)) { @@ -303,6 +317,7 @@ static std::vector> slices_to_regions( } // Second perform region clipping and assignment in parallel. + //其次,并行执行区域裁剪和分配。 if (! zs_complex.empty()) { std::vector> layer_ranges_regions_to_slices(print_object_regions.layer_ranges.size(), std::vector()); for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) { @@ -318,9 +333,11 @@ static std::vector> slices_to_regions( float z = zs_complex[range.begin()].second; auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z); // Per volume_regions slices at this Z height. + //按体积_区域在此Z高度处切片。 struct RegionSlice { ExPolygons expolygons; // Identifier of this region in PrintObjectRegions::all_regions + //PrintObjectRegions中此区域的标识符::all_regions int region_id; ObjectID volume_id; bool operator<(const RegionSlice &rhs) const { @@ -328,6 +345,8 @@ static std::vector> slices_to_regions( bool rhs_empty = rhs.region_id < 0 || rhs.expolygons.empty(); // Sort the empty items to the end of the list. // Sort by region_id & volume_id lexicographically. + //将空项目排序到列表末尾。 + //按region_id和volume_id按字母顺序排序。 return ! this_empty && (rhs_empty || (this->region_id < rhs.region_id || (this->region_id == rhs.region_id && volume_id < volume_id))); } }; @@ -368,6 +387,7 @@ static std::vector> slices_to_regions( { std::vector &layer_range_regions_to_slices = layer_ranges_regions_to_slices[it_layer_range - print_object_regions.layer_ranges.begin()]; // Per volume_regions slices at thiz Z height. + //按体积_区域在Z高度处切片。 temp_slices.clear(); temp_slices.reserve(layer_range.volume_regions.size()); for (VolumeSlices* &slices : layer_range_regions_to_slices) { @@ -392,12 +412,15 @@ static std::vector> slices_to_regions( } if (next_region_same_modifier) // To be used in the following iteration. + //将在以下迭代中使用。 temp_slices[idx_region + 1].expolygons = std::move(source); } else if ((region.model_volume->is_model_part() && clip_multipart_objects) || region.model_volume->is_negative_volume()) { // Clip every non-zero region preceding it. + //剪切它前面的每个非零区域。 for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2) if (! temp_slices[idx_region2].expolygons.empty()) { // Skip trim_overlap for now, because it slow down the performace so much for some special cases + //暂时跳过trim_overlap,因为在某些特殊情况下,它会大大降低性能 #if 1 if (const PrintObjectRegions::VolumeRegion& region2 = layer_range.volume_regions[idx_region2]; !region2.model_volume->is_negative_volume() && overlap_in_xy(*region.bbox, *region2.bbox)) @@ -415,12 +438,15 @@ static std::vector> slices_to_regions( } } // Sort by region_id, push empty slices to the end. + //按region_id排序,将空切片推到末尾。 std::sort(temp_slices.begin(), temp_slices.end()); // Remove the empty slices. temp_slices.erase(std::find_if(temp_slices.begin(), temp_slices.end(), [](const auto &slice) { return slice.region_id == -1 || slice.expolygons.empty(); }), temp_slices.end()); // Merge slices and store them to the output. + //合并切片并将其存储到输出中。 for (int i = 0; i < int(temp_slices.size());) { // Find a range of temp_slices with the same region_id. + //查找具有相同region_id的temp_slices范围。 int j = i; bool merged = false; ExPolygons &expolygons = temp_slices[i].expolygons; @@ -436,6 +462,7 @@ static std::vector> slices_to_regions( // Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters // for example. + //如果发生这种情况,不要团结各地区!clip_multipart_objects。在这种情况下,用户有责任处理区域重叠。事实上,例如,人们可能会故意让这些区域重叠以产生交叉边界。 if (merged && clip_multipart_objects) expolygons = closing_ex(expolygons, float(scale_(EPSILON))); slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); @@ -565,6 +592,7 @@ bool groupingVolumes(std::vector objSliceByVolume, std::vector findPartVolumes(const std::vector& objSliceByVolume, ModelVolumePtrs model_volumes) { std::vector outPut; for (const auto& vs : objSliceByVolume) { @@ -781,11 +809,20 @@ void groupingVolumesForBrim(PrintObject* object, LayerPtrs& layers, int firstLay // 5) Applies size compensation (offsets the slices in XY plane) // 6) Replaces bad slices by the slices reconstructed from the upper/lower layer // Resulting expolygons of layer regions are marked as Internal. +//由make_perimeters()调用 +//1)决定层的Z位置, +//2)初始化层及其区域 +//3)分割对象网格 +//4)对修改器网格进行切片,并根据修改器网格的切片对对象网格的切片进行重新分类 +//5)应用尺寸补偿(偏移XY平面中的切片) +//6)用从上层/下层重建的切片替换坏切片 +//层区域的扩展结果标记为内部。 void PrintObject::slice() { if (! this->set_started(posSlice)) return; //BBS: add flag to reload scene for shell rendering + //BBS:添加标志以重新加载场景进行shell渲染 m_print->set_status(5, L("Slicing mesh"), PrintBase::SlicingStatus::RELOAD_SCENE); std::vector layer_height_profile; this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile); @@ -800,10 +837,14 @@ void PrintObject::slice() #if 1 // Fix the model. //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. + //修复模型。 + //FIXME是合适的地方吗?它在UI上重复完成,现在在后端完成。 std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy); m_print->throw_if_canceled(); //BBS: send warning message to slicing callback // This warning is inaccurate, because the empty layers may have been replaced, or the model has supports. + //BBS:向切片回调发送警告消息 + //此警告不准确,因为空层可能已被替换,或者模型有支撑。 //if (!warning.empty()) { // BOOST_LOG_TRIVIAL(info) << warning; // this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers); @@ -811,9 +852,11 @@ void PrintObject::slice() #endif // BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim + //BBS:存储在层中的实际第一层切片按卷组重新排序,并将用于生成边缘 groupingVolumesForBrim(this, m_layers, firstLayerReplacedBy); // Update bounding boxes, back up raw slices of complex models. + //更新边界框,备份复杂模型的原始切片。 tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { @@ -971,6 +1014,15 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance // Resulting expolygons of layer regions are marked as Internal. // // this should be idempotent +//1)决定层的Z位置, +//2)初始化层及其区域 +//3)分割对象网格 +//4)对修改器网格进行切片,并根据修改器网格的切片对对象网格的切片进行重新分类 +//5)应用尺寸补偿(偏移XY平面中的切片) +//6)用从上层/下层重建的切片替换坏切片 +//层区域的扩展结果标记为内部。 +// +//这应该是幂等的 void PrintObject::slice_volumes() { BOOST_LOG_TRIVIAL(info) << "Slicing volumes..." << log_memory_info(); @@ -978,8 +1030,10 @@ void PrintObject::slice_volumes() const auto throw_on_cancel_callback = std::function([print](){ print->throw_if_canceled(); }); // Clear old LayerRegions, allocate for new PrintRegions. + //清除旧的LayerRegions,为新的PrintRegions分配。 for (Layer* layer : m_layers) { //BBS: should delete all LayerRegionPtr to avoid memory leak + //BBS:应删除所有LayerRegionPtr以避免内存泄漏 while (!layer->m_regions.empty()) { if (layer->m_regions.back()) delete layer->m_regions.back(); @@ -999,6 +1053,7 @@ void PrintObject::slice_volumes() } //BBS: "model_part" volumes are grouded according to their connections + //BBS:“model_part”卷根据其连接进行分组 //const auto scaled_resolution = scaled(print->config().resolution.value); //firstLayerObjSliceByVolume = findPartVolumes(objSliceByVolume, this->model_object()->volumes); //groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups, scaled_resolution); @@ -1030,12 +1085,14 @@ void PrintObject::slice_volumes() m_print->throw_if_canceled(); // Is any ModelVolume MMU painted? + //是否有任何ModelVolume MMU涂漆? if (const auto& volumes = this->model_object()->volumes; m_print->config().filament_diameter.size() > 1 && // BBS std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) { // If XY Size compensation is also enabled, notify the user that XY Size compensation // would not be used because the object is multi-material painted. + //如果还启用了XY尺寸补偿,请通知用户由于对象是多材质绘制的,因此不会使用XY尺寸补偿。 if (m_config.xy_hole_compensation.value != 0.f || m_config.xy_contour_compensation.value != 0.f) { this->active_step_add_warning( PrintStateBase::WarningLevel::CRITICAL, @@ -1052,16 +1109,20 @@ void PrintObject::slice_volumes() BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin"; { // Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing. + //补偿值,按比例计算。此处仅应用负缩放,因为在切片过程中已经应用了正缩放。 const size_t num_extruders = print->config().filament_diameter.size(); const auto xy_hole_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled(0.f) : scaled(m_config.xy_hole_compensation.value); const auto xy_contour_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled(0.f) : scaled(m_config.xy_contour_compensation.value); const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ? // Only enable Elephant foot compensation if printing directly on the print bed. + //仅在直接在打印床上打印时启用象脚补偿。 float(scale_(m_config.elefant_foot_compensation.value)) : 0.f; // Uncompensated slices for the first layer in case the Elephant foot compensation is applied. + //在应用象脚补偿的情况下,第一层的无补偿切片。 ExPolygons lslices_1st_layer; //BBS: this part has been changed a lot to support seperated contour and hole size compensation + //BBS:这部分已经做了很多更改,以支持单独的轮廓和孔尺寸补偿 tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, xy_hole_scaled, xy_contour_scaled, elephant_foot_compensation_scaled, &lslices_1st_layer](const tbb::blocked_range& range) { @@ -1069,13 +1130,17 @@ void PrintObject::slice_volumes() m_print->throw_if_canceled(); Layer *layer = m_layers[layer_id]; // Apply size compensation and perform clipping of multi-part objects. + //应用尺寸补偿并对多部分对象进行剪裁。 float elfoot = (layer_id == 0) ? elephant_foot_compensation_scaled : 0.f; if (layer->m_regions.size() == 1) { // Optimized version for a single region layer. // Single region, growing or shrinking. + //针对单个区域层的优化版本。 + //单一地区,增长或萎缩。 LayerRegion *layerm = layer->m_regions.front(); if (elfoot > 0) { // Apply the elephant foot compensation and store the 1st layer slices without the Elephant foot compensation applied. + //应用象脚补偿,并存储未应用象脚赔偿的第一层切片。 lslices_1st_layer = to_expolygons(std::move(layerm->slices.surfaces)); if (xy_contour_scaled > 0 || xy_hole_scaled > 0) { lslices_1st_layer = _shrink_contour_holes(std::max(0.f, xy_contour_scaled), @@ -1094,6 +1159,7 @@ void PrintObject::slice_volumes() stInternal); } else { // Apply the XY contour and hole size compensation. + //应用XY轮廓和孔尺寸补偿。 if (xy_contour_scaled != 0.0f || xy_hole_scaled != 0.0f) { ExPolygons expolygons = to_expolygons(std::move(layerm->slices.surfaces)); if (xy_contour_scaled > 0 || xy_hole_scaled > 0) { @@ -1116,12 +1182,15 @@ void PrintObject::slice_volumes() if (max_growth > 0) { //BBS: merge polygons because region can cut "holes". //Then, cut them to give them again later to their region + //BBS:合并多边形,因为该区域可以切割“孔”。 + //然后,把它们剪下来,稍后再送给它们所在的地区 merged_poly_for_holes_growing = layer->merged(float(SCALED_EPSILON)); merged_poly_for_holes_growing = _shrink_contour_holes(std::max(0.f, xy_contour_scaled), std::max(0.f, xy_hole_scaled), union_ex(merged_poly_for_holes_growing)); // BBS: clipping regions, priority is given to the first regions. + //BBS:剪切区域,优先剪切第一个区域。 Polygons processed; for (size_t region_id = 0; region_id < layer->regions().size(); ++region_id) { ExPolygons slices = to_expolygons(std::move(layer->m_regions[region_id]->slices.surfaces)); @@ -1130,16 +1199,19 @@ void PrintObject::slice_volumes() } //BBS: Trim by the slices of already processed regions. + //BBS:按已加工区域的切片进行修剪。 if (region_id > 0) slices = diff_ex(to_polygons(std::move(slices)), processed); if (region_id + 1 < layer->regions().size()) // Collect the already processed regions to trim the to be processed regions. + //收集已处理的区域以修剪待处理的区域。 polygons_append(processed, slices); layer->m_regions[region_id]->slices.set(std::move(slices), stInternal); } } if (min_growth < 0.f || elfoot > 0.f) { // Apply the negative XY compensation. (the ones that is <0) + //应用负XY补偿。(小于0的那些) ExPolygons trimming; static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5); if (elfoot > 0.f) { @@ -1154,8 +1226,10 @@ void PrintObject::slice_volumes() std::min(0.f, xy_hole_scaled), trimming); //BBS: trim surfaces + //BBS:修整表面 for (size_t region_id = 0; region_id < layer->regions().size(); ++region_id) { // BBS: split trimming result by region + //BBS:按地区划分修剪结果 ExPolygons contour_exp = to_expolygons(std::move(layer->regions()[region_id]->slices.surfaces)); layer->regions()[region_id]->slices.set(intersection_ex(contour_exp, to_polygons(trimming)), stInternal); @@ -1163,15 +1237,20 @@ void PrintObject::slice_volumes() } } // Merge all regions' slices to get islands, chain them by a shortest path. + //合并所有区域的切片以获得岛屿,用最短路径将它们链接起来。 layer->make_slices(); } }); if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) { // The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value. // Store the uncompensated value there. + //大象脚已经得到补偿,因此第一层的切片缩小了大象脚补偿值。 + //将未补偿的价值存储在那里。 assert(m_layers.front()->id() == 0); //BBS: sort the lslices_1st_layer according to shortest path before saving //Otherwise the travel of first layer would be mess. + //BBS:保存前按最短路径对lslices_1st_layer进行排序 + //否则,第一层的旅行会一团糟。 Points ordering_points; ordering_points.reserve(lslices_1st_layer.size()); for (const ExPolygon& ex : lslices_1st_layer) diff --git a/libslic3r/Slicing.cpp b/libslic3r/Slicing.cpp index d81684e..7d768c6 100644 --- a/libslic3r/Slicing.cpp +++ b/libslic3r/Slicing.cpp @@ -723,6 +723,7 @@ bool adjust_layer_series_to_align_object_height(const SlicingParameters &slicing } // Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. +//将对象层作为低/高层边界对生成,并存储到线性向量中。 std::vector generate_object_layers( const SlicingParameters &slicing_params, const std::vector &layer_height_profile, @@ -743,6 +744,7 @@ std::vector generate_object_layers( size_t idx_layer_height_profile = 0; // loop until we have at least one layer and the max slice_z reaches the object height + //循环,直到我们至少有一个层,并且最大slice_z达到对象高度 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; diff --git a/libslic3r/TriangleMeshSlicer.cpp b/libslic3r/TriangleMeshSlicer.cpp index 378c148..99e4186 100644 --- a/libslic3r/TriangleMeshSlicer.cpp +++ b/libslic3r/TriangleMeshSlicer.cpp @@ -1878,20 +1878,26 @@ std::vector slice_mesh( // Instead of edge identifiers, one shall use a sorted pair of edge vertex indices. // However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have // to make sure that no code relies on it. + //FIXME facets_edges可能不需要,计算成本相当高。 + //应使用排序后的一对边顶点索引来代替边标识符。 + //然而,facets_edges只为两个三角形分配一个边ID,因此在分解facets_edge时,必须确保没有代码依赖于它。 std::vector face_edge_ids = its_face_edge_ids(mesh); if (zs.size() <= 1) { // It likely is not worthwile to copy the vertices. Apply the transformation in place. + //复制顶点可能不值得。将转换应用到位。 if (is_identity(params.trafo)) { lines = slice_make_lines( mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled(p.x()), scaled(p.y()), p.z()); }, mesh.indices, face_edge_ids, zs, throw_on_cancel); } else { // Transform the vertices, scale up in XY, not in Z. + //变换顶点,在XY方向上放大,而不是在Z方向上。 Transform3f tf = make_trafo_for_slicing(params.trafo); lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel); } } else { // Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation. + //在XY中复制和缩放顶点,不要在Z中缩放。可能会应用变换。 lines = slice_make_lines( transform_mesh_vertices_for_slicing(mesh, params.trafo), [](const Vec3f &p) { return p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel); diff --git a/libslic3r/VariableWidth.cpp b/libslic3r/VariableWidth.cpp index b7c5fd1..4dab8d3 100644 --- a/libslic3r/VariableWidth.cpp +++ b/libslic3r/VariableWidth.cpp @@ -98,6 +98,7 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl } //BBS: new function to filter width to avoid too fragmented segments +//BBS:新功能过滤宽度,避免片段过于碎片化 static ExtrusionPaths thick_polyline_to_extrusion_paths_2(const ThickPolyline& thick_polyline, ExtrusionRole role, const Flow& flow, const float tolerance) { ExtrusionPaths paths; @@ -216,6 +217,7 @@ void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const F // This value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance. + //该值决定了自适应宽度的粒度,因为G代码不允许在一次移动中进行可变拉伸;该值仅影响分段数量,在应用此公差之前,应进行任何修剪。 const float tolerance = float(scale_(0.05)); for (const ThickPolyline& p : polylines) { ExtrusionPaths paths = thick_polyline_to_extrusion_paths_2(p, role, flow, tolerance); diff --git a/slic3r/GUI/BackgroundSlicingProcess.cpp b/slic3r/GUI/BackgroundSlicingProcess.cpp index 8be1494..82cf2c2 100644 --- a/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -192,18 +192,22 @@ std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::f // This function may one day be merged into the Print, but historically the print was separated // from the G-code generator. +//此功能可能有一天会合并到打印中,但历史上打印与G代码生成器是分开的。 void BackgroundSlicingProcess::process_fff() { assert(m_print == m_fff_print); PresetBundle &preset_bundle = *wxGetApp().preset_bundle; m_fff_print->set_BBL_Printer(preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle)); //BBS: add the logic to process from an existed gcode file + //BBS:从已有的gcode文件中添加逻辑以进行处理 if (m_print->finished()) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: skip slicing, to process previous gcode file")%__LINE__; m_fff_print->set_status(80, _utf8(L("Processing G-Code from Previous file..."))); wxCommandEvent evt(m_event_slicing_completed_id); // Post the Slicing Finished message for the G-code viewer to update. // Passing the timestamp + //发布“切片完成”消息,以便G代码查看器进行更新。 + //传递时间戳 evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psSlicingFinished).timestamp)); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); @@ -219,6 +223,8 @@ void BackgroundSlicingProcess::process_fff() else { //BBS: reset the gcode before reload_print in slicing_completed event processing //FIX the gcode rename failed issue + //BBS:在slicing_colleged事件处理中reload_print之前重置gcode + //修复gcode重命名失败的问题 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: will start slicing, reset gcode_result %2% firstly")%__LINE__%m_gcode_result; m_gcode_result->reset(); @@ -229,10 +235,13 @@ void BackgroundSlicingProcess::process_fff() wxCommandEvent evt(m_event_slicing_completed_id); // Post the Slicing Finished message for the G-code viewer to update. // Passing the timestamp + //发布“切片完成”消息,以便G代码查看器进行更新。 + //传递时间戳 evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psSlicingFinished).timestamp)); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); //BBS: add plate index into render params + //BBS:在渲染参数中添加板块索引 m_temp_output_path = this->get_current_plate()->get_tmp_gcode_path(); m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); }); finalize_gcode(); diff --git a/slic3r/GUI/Plater.cpp b/slic3r/GUI/Plater.cpp index 5a7d5b8..56651ec 100644 --- a/slic3r/GUI/Plater.cpp +++ b/slic3r/GUI/Plater.cpp @@ -9447,6 +9447,8 @@ void Plater::_calib_pa_pattern(const Calib_Params ¶ms) wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty(); wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->reload_config(); const DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(); PresetBundle * preset_bundle = wxGetApp().preset_bundle; @@ -9502,6 +9504,8 @@ void Plater::_calib_pa_tower(const Calib_Params ¶ms) wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_PRINTER)->reload_config(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->reload_config(); auto new_height = std::ceil((params.end - params.start) / params.step) + 1; auto obj_bb = model().objects[0]->bounding_box(); @@ -9616,6 +9620,8 @@ void Plater::calib_flowrate(int pass) wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_PRINTER)->reload_config(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->reload_config(); try { json js; @@ -9669,6 +9675,8 @@ void Plater::calib_temp(const Calib_Params ¶ms) wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_dirty(); wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->reload_config(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->reload_config(); // cut upper auto obj_bb = model().objects[0]->bounding_box(); @@ -9759,6 +9767,8 @@ void Plater::calib_max_vol_speed(const Calib_Params ¶ms) wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->reload_config(); wxGetApp().get_tab(Preset::TYPE_PRINTER)->reload_config(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->reload_config(); // cut upper auto obj_bb = obj->bounding_box(); @@ -9866,6 +9876,8 @@ void Plater::calib_VFA(const Calib_Params ¶ms) wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_dirty(); wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings(); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_CONFIG)->update_ui_from_settings(); // cut upper auto obj_bb = model().objects[0]->bounding_box(); diff --git a/slic3r/GUI/Tab.cpp b/slic3r/GUI/Tab.cpp index d302aeb..eacf2b4 100644 --- a/slic3r/GUI/Tab.cpp +++ b/slic3r/GUI/Tab.cpp @@ -1971,12 +1971,12 @@ void TabPrint::build() //optgroup = page->new_optgroup(L("Wall generator"), L"param_wall"); //optgroup->append_single_option_line("wall_generator", "wall-generator"); - optgroup->append_single_option_line("wall_transition_angle"); - optgroup->append_single_option_line("wall_transition_filter_deviation"); - optgroup->append_single_option_line("wall_transition_length"); - optgroup->append_single_option_line("wall_distribution_count"); - optgroup->append_single_option_line("min_bead_width"); - optgroup->append_single_option_line("min_feature_size"); + //optgroup->append_single_option_line("wall_transition_angle"); + //optgroup->append_single_option_line("wall_transition_filter_deviation"); + //optgroup->append_single_option_line("wall_transition_length"); + //optgroup->append_single_option_line("wall_distribution_count"); + //optgroup->append_single_option_line("min_bead_width"); + //->append_single_option_line("min_feature_size"); //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); //optgroup->append_single_option_line("wall_sequence"); @@ -4378,7 +4378,10 @@ void TabPrinter::update_sla() void TabConfig::build() { - m_presets = &m_preset_bundle->configs; + + if (m_presets == nullptr) + m_presets = &m_preset_bundle->configs; + load_initial_data(); auto page = add_options_page(L("Quality"), "spool"); auto optgroup = page->new_optgroup("", L"param_layer_height"); @@ -4428,7 +4431,6 @@ void TabConfig::build() { optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); optgroup->append_single_option_line("support_interface_top_layers", "support#base-pattern"); optgroup->append_single_option_line("support_interface_bottom_layers", "support#base-pattern"); - //you cuo wu -- zan ding //optgroup->append_single_option_line("support_interface_spacing", "support#base-pattern"); @@ -4508,7 +4510,7 @@ void TabConfig::build() { page = add_options_page(L("Others"), "advanced"); optgroup = page->new_optgroup("", L"param_adhension"); optgroup->append_single_option_line("slicing_mode"); - optgroup->append_single_option_line("fibre_feed_rate"); + //optgroup->append_single_option_line("fibre_feed_rate"); optgroup->append_single_option_line("print_sequence", "sequent-print"); optgroup->append_single_option_line("spiral_mode", "spiral-vase"); optgroup->append_single_option_line("spiral_mode_smooth", "spiral-vase#smooth"); @@ -4526,6 +4528,7 @@ void TabConfig::reload_config() { //this->compatible_widget_reload(m_compatible_printers); //this->compatible_widget_reload(m_compatible_prints); + this->compatible_widget_reload(m_compatible_printers); Tab::reload_config(); } //void TabConfig::update_description_lines() @@ -4545,10 +4548,12 @@ void TabConfig::toggle_options() { if (!m_active_page) return; - bool is_BBL_printer = false; if (m_preset_bundle) { - is_BBL_printer = m_preset_bundle->configs.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle); + bool is_BBL_printer = m_preset_bundle->configs.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle); + m_config_manipulation.set_is_BBL_Printer(is_BBL_printer); } + + m_config_manipulation.toggle_print_fff_options(m_config, m_type < Preset::TYPE_COUNT); bool enable_support = m_preset_bundle->prints.get_edited_preset().config.opt_bool("enable_support"); for (auto el : { "support_style", "support_base_pattern", @@ -4779,14 +4784,22 @@ void TabConfig::toggle_options() { } } void TabConfig::update() { + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) + return; // ys_FIXME + m_update_cnt++; - toggle_options(); - + m_config_manipulation.update_print_fff_config(m_config, m_type < Preset::TYPE_COUNT, m_type == Preset::TYPE_PLATE); + m_parent->Layout(); m_update_cnt--; + + if (m_update_cnt == 0) { + toggle_options(); + if (m_type != Preset::TYPE_MODEL && !wxGetApp().plater()->inside_snapshot_capture()) + wxGetApp().obj_list()->update_and_show_object_settings_item(); - if (m_update_cnt == 0) wxGetApp().mainframe->on_config_changed(m_config); + } } void TabConfig::clear_pages() { Tab::clear_pages(); diff --git a/slic3r/Utils/CalibUtils.cpp b/slic3r/Utils/CalibUtils.cpp index 05cced0..e723006 100644 --- a/slic3r/Utils/CalibUtils.cpp +++ b/slic3r/Utils/CalibUtils.cpp @@ -508,6 +508,7 @@ bool CalibUtils::calib_flowrate(int pass, const CalibInfo &calib_info, wxString DynamicConfig print_config = calib_info.print_prest->config; DynamicConfig filament_config = calib_info.filament_prest->config; DynamicConfig printer_config = calib_info.printer_prest->config; + DynamicConfig config_config = calib_info.config_prest->config; /// --- scale --- // model is created for a 0.4 nozzle, scale z with nozzle size. @@ -577,6 +578,7 @@ bool CalibUtils::calib_flowrate(int pass, const CalibInfo &calib_info, wxString full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); Calib_Params params; params.mode = CalibMode::Calib_Flow_Rate; @@ -618,12 +620,14 @@ void CalibUtils::calib_pa_pattern(const CalibInfo &calib_info, Model& model) DynamicPrintConfig& print_config = calib_info.print_prest->config; DynamicPrintConfig& filament_config = calib_info.filament_prest->config; DynamicPrintConfig& printer_config = calib_info.printer_prest->config; + DynamicPrintConfig& config_config = calib_info.config_prest->config; DynamicPrintConfig full_config; full_config.apply(FullPrintConfig::defaults()); full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); float nozzle_diameter = printer_config.option("nozzle_diameter")->get_at(0); @@ -652,6 +656,7 @@ void CalibUtils::calib_pa_pattern(const CalibInfo &calib_info, Model& model) full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); Vec3d plate_origin(0, 0, 0); CalibPressureAdvancePattern pa_pattern(calib_info.params, full_config, true, model, plate_origin); @@ -688,6 +693,7 @@ bool CalibUtils::calib_generic_PA(const CalibInfo &calib_info, wxString &error_m DynamicPrintConfig print_config = calib_info.print_prest->config; DynamicPrintConfig filament_config = calib_info.filament_prest->config; DynamicPrintConfig printer_config = calib_info.printer_prest->config; + DynamicPrintConfig config_config = calib_info.config_prest->config; filament_config.set_key_value("curr_bed_type", new ConfigOptionEnum(calib_info.bed_type)); @@ -696,6 +702,8 @@ bool CalibUtils::calib_generic_PA(const CalibInfo &calib_info, wxString &error_m full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); + if (!process_and_store_3mf(&model, full_config, params, error_message)) return false; @@ -780,6 +788,7 @@ void CalibUtils::calib_temptue(const CalibInfo &calib_info, wxString &error_mess DynamicPrintConfig print_config = calib_info.print_prest->config; DynamicPrintConfig filament_config = calib_info.filament_prest->config; DynamicPrintConfig printer_config = calib_info.printer_prest->config; + DynamicPrintConfig config_config = calib_info.config_prest->config; auto start_temp = lround(params.start); filament_config.set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(1, (int) start_temp)); @@ -797,6 +806,7 @@ void CalibUtils::calib_temptue(const CalibInfo &calib_info, wxString &error_mess full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); process_and_store_3mf(&model, full_config, params, error_message); if (!error_message.empty()) @@ -818,6 +828,7 @@ void CalibUtils::calib_max_vol_speed(const CalibInfo &calib_info, wxString &erro DynamicPrintConfig print_config = calib_info.print_prest->config; DynamicPrintConfig filament_config = calib_info.filament_prest->config; DynamicPrintConfig printer_config = calib_info.printer_prest->config; + DynamicPrintConfig config_config = calib_info.config_prest->config; auto obj = model.objects[0]; auto bed_shape = printer_config.option("printable_area")->values; @@ -877,6 +888,7 @@ void CalibUtils::calib_max_vol_speed(const CalibInfo &calib_info, wxString &erro full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); process_and_store_3mf(&model, full_config, new_params, error_message); if (!error_message.empty()) @@ -898,6 +910,7 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message) DynamicPrintConfig print_config = calib_info.print_prest->config; DynamicPrintConfig filament_config = calib_info.filament_prest->config; DynamicPrintConfig printer_config = calib_info.printer_prest->config; + DynamicPrintConfig config_config = calib_info.config_prest->config; filament_config.set_key_value("slow_down_layer_time", new ConfigOptionInts{0}); filament_config.set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200}); @@ -935,6 +948,7 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message) full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); process_and_store_3mf(&model, full_config, params, error_message); if (!error_message.empty()) @@ -956,6 +970,7 @@ void CalibUtils::calib_retraction(const CalibInfo &calib_info, wxString &error_m DynamicPrintConfig print_config = calib_info.print_prest->config; DynamicPrintConfig filament_config = calib_info.filament_prest->config; DynamicPrintConfig printer_config = calib_info.printer_prest->config; + DynamicPrintConfig config_config = calib_info.config_prest->config; auto obj = model.objects[0]; @@ -986,6 +1001,7 @@ void CalibUtils::calib_retraction(const CalibInfo &calib_info, wxString &error_m full_config.apply(print_config); full_config.apply(filament_config); full_config.apply(printer_config); + full_config.apply(config_config); process_and_store_3mf(&model, full_config, params, error_message); if (!error_message.empty()) diff --git a/slic3r/Utils/CalibUtils.hpp b/slic3r/Utils/CalibUtils.hpp index 980ba30..0be696b 100644 --- a/slic3r/Utils/CalibUtils.hpp +++ b/slic3r/Utils/CalibUtils.hpp @@ -19,6 +19,7 @@ public: Preset* printer_prest; Preset* filament_prest; Preset* print_prest; + Preset* config_prest; BedType bed_type; std::string dev_id; std::string select_ams;