Merge remote-tracking branch 'remote/master' into SoftFever

This commit is contained in:
SoftFever 2022-09-03 00:17:59 +08:00
commit 4fd174175c
298 changed files with 13879 additions and 6228 deletions

View file

@ -446,6 +446,57 @@ namespace detail {
}
}
// Real-time collision detection, Ericson, Chapter 5
template<typename Vector>
static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
{
using Scalar = typename Vector::Scalar;
// Check if P in vertex region outside A
Vector ab = b - a;
Vector ac = c - a;
Vector ap = p - a;
Scalar d1 = ab.dot(ap);
Scalar d2 = ac.dot(ap);
if (d1 <= 0 && d2 <= 0)
return a;
// Check if P in vertex region outside B
Vector bp = p - b;
Scalar d3 = ab.dot(bp);
Scalar d4 = ac.dot(bp);
if (d3 >= 0 && d4 <= d3)
return b;
// Check if P in edge region of AB, if so return projection of P onto AB
Scalar vc = d1*d4 - d3*d2;
if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
Scalar v = d1 / (d1 - d3);
return a + v * ab;
}
// Check if P in vertex region outside C
Vector cp = p - c;
Scalar d5 = ab.dot(cp);
Scalar d6 = ac.dot(cp);
if (d6 >= 0 && d5 <= d6)
return c;
// Check if P in edge region of AC, if so return projection of P onto AC
Scalar vb = d5*d2 - d1*d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
Scalar w = d2 / (d2 - d6);
return a + w * ac;
}
// Check if P in edge region of BC, if so return projection of P onto BC
Scalar va = d3*d6 - d5*d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
return b + w * (c - b);
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
Scalar denom = Scalar(1.0) / (va + vb + vc);
Scalar v = vb * denom;
Scalar w = vc * denom;
return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
};
// Nothing to do with COVID-19 social distancing.
template<typename AVertexType, typename AIndexedFaceType, typename ATreeType, typename AVectorType>
struct IndexedTriangleSetDistancer {
@ -453,74 +504,36 @@ namespace detail {
using IndexedFaceType = AIndexedFaceType;
using TreeType = ATreeType;
using VectorType = AVectorType;
using ScalarType = typename VectorType::Scalar;
const std::vector<VertexType> &vertices;
const std::vector<IndexedFaceType> &faces;
const TreeType &tree;
const VectorType origin;
inline VectorType closest_point_to_origin(size_t primitive_index,
ScalarType& squared_distance){
const auto &triangle = this->faces[primitive_index];
VectorType closest_point = closest_point_to_triangle<VectorType>(origin,
this->vertices[triangle(0)].template cast<ScalarType>(),
this->vertices[triangle(1)].template cast<ScalarType>(),
this->vertices[triangle(2)].template cast<ScalarType>());
squared_distance = (origin - closest_point).squaredNorm();
return closest_point;
}
};
// Real-time collision detection, Ericson, Chapter 5
template<typename Vector>
static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
{
using Scalar = typename Vector::Scalar;
// Check if P in vertex region outside A
Vector ab = b - a;
Vector ac = c - a;
Vector ap = p - a;
Scalar d1 = ab.dot(ap);
Scalar d2 = ac.dot(ap);
if (d1 <= 0 && d2 <= 0)
return a;
// Check if P in vertex region outside B
Vector bp = p - b;
Scalar d3 = ab.dot(bp);
Scalar d4 = ac.dot(bp);
if (d3 >= 0 && d4 <= d3)
return b;
// Check if P in edge region of AB, if so return projection of P onto AB
Scalar vc = d1*d4 - d3*d2;
if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
Scalar v = d1 / (d1 - d3);
return a + v * ab;
}
// Check if P in vertex region outside C
Vector cp = p - c;
Scalar d5 = ab.dot(cp);
Scalar d6 = ac.dot(cp);
if (d6 >= 0 && d5 <= d6)
return c;
// Check if P in edge region of AC, if so return projection of P onto AC
Scalar vb = d5*d2 - d1*d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
Scalar w = d2 / (d2 - d6);
return a + w * ac;
}
// Check if P in edge region of BC, if so return projection of P onto BC
Scalar va = d3*d6 - d5*d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
return b + w * (c - b);
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
Scalar denom = Scalar(1.0) / (va + vb + vc);
Scalar v = vb * denom;
Scalar w = vc * denom;
return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
};
template<typename IndexedTriangleSetDistancerType, typename Scalar>
static inline Scalar squared_distance_to_indexed_triangle_set_recursive(
IndexedTriangleSetDistancerType &distancer,
template<typename IndexedPrimitivesDistancerType, typename Scalar>
static inline Scalar squared_distance_to_indexed_primitives_recursive(
IndexedPrimitivesDistancerType &distancer,
size_t node_idx,
Scalar low_sqr_d,
Scalar up_sqr_d,
size_t &i,
Eigen::PlainObjectBase<typename IndexedTriangleSetDistancerType::VectorType> &c)
Eigen::PlainObjectBase<typename IndexedPrimitivesDistancerType::VectorType> &c)
{
using Vector = typename IndexedTriangleSetDistancerType::VectorType;
using Vector = typename IndexedPrimitivesDistancerType::VectorType;
if (low_sqr_d > up_sqr_d)
return low_sqr_d;
@ -538,13 +551,9 @@ namespace detail {
assert(node.is_valid());
if (node.is_leaf())
{
const auto &triangle = distancer.faces[node.idx];
Vector c_candidate = closest_point_to_triangle<Vector>(
distancer.origin,
distancer.vertices[triangle(0)].template cast<Scalar>(),
distancer.vertices[triangle(1)].template cast<Scalar>(),
distancer.vertices[triangle(2)].template cast<Scalar>());
set_min((c_candidate - distancer.origin).squaredNorm(), node.idx, c_candidate);
Scalar sqr_dist;
Vector c_candidate = distancer.closest_point_to_origin(node.idx, sqr_dist);
set_min(sqr_dist, node.idx, c_candidate);
}
else
{
@ -561,7 +570,7 @@ namespace detail {
{
size_t i_left;
Vector c_left = c;
Scalar sqr_d_left = squared_distance_to_indexed_triangle_set_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
Scalar sqr_d_left = squared_distance_to_indexed_primitives_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
set_min(sqr_d_left, i_left, c_left);
looked_left = true;
};
@ -569,13 +578,13 @@ namespace detail {
{
size_t i_right;
Vector c_right = c;
Scalar sqr_d_right = squared_distance_to_indexed_triangle_set_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
Scalar sqr_d_right = squared_distance_to_indexed_primitives_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
set_min(sqr_d_right, i_right, c_right);
looked_right = true;
};
// must look left or right if in box
using BBoxScalar = typename IndexedTriangleSetDistancerType::TreeType::BoundingBox::Scalar;
using BBoxScalar = typename IndexedPrimitivesDistancerType::TreeType::BoundingBox::Scalar;
if (node_left.bbox.contains(distancer.origin.template cast<BBoxScalar>()))
look_left();
if (node_right.bbox.contains(distancer.origin.template cast<BBoxScalar>()))
@ -709,10 +718,15 @@ inline bool intersect_ray_all_hits(
origin, dir, VectorType(dir.cwiseInverse()),
eps }
};
if (! tree.empty()) {
if (tree.empty()) {
hits.clear();
} else {
// Reusing the output memory if there is some memory already pre-allocated.
ray_intersector.hits = std::move(hits);
ray_intersector.hits.clear();
ray_intersector.hits.reserve(8);
detail::intersect_ray_recursive_all_hits(ray_intersector, 0);
std::swap(hits, ray_intersector.hits);
hits = std::move(ray_intersector.hits);
std::sort(hits.begin(), hits.end(), [](const auto &l, const auto &r) { return l.t < r.t; });
}
return ! hits.empty();
@ -742,7 +756,7 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set(
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
{ vertices, faces, tree, point };
return tree.empty() ? Scalar(-1) :
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
}
// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
@ -759,22 +773,22 @@ inline bool is_any_triangle_in_radius(
const TreeType &tree,
// Point to which the closest point on the indexed triangle set is searched for.
const VectorType &point,
// Maximum distance in which triangle is search for
typename VectorType::Scalar &max_distance)
//Square of maximum distance in which triangle is searched for
typename VectorType::Scalar &max_distance_squared)
{
using Scalar = typename VectorType::Scalar;
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
{ vertices, faces, tree, point };
size_t hit_idx;
VectorType hit_point = VectorType::Ones() * (std::nan(""));
size_t hit_idx;
VectorType hit_point = VectorType::Ones() * (NaN<typename VectorType::Scalar>);
if(tree.empty())
{
return false;
}
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance, hit_idx, hit_point);
detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_distance_squared, hit_idx, hit_point);
return hit_point.allFinite();
}

View file

@ -0,0 +1,112 @@
#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
#include "libslic3r/Point.hpp"
#include "libslic3r/EdgeGrid.hpp"
#include "libslic3r/AABBTreeIndirect.hpp"
#include "libslic3r/Line.hpp"
namespace Slic3r {
namespace AABBTreeLines {
namespace detail {
template<typename ALineType, typename ATreeType, typename AVectorType>
struct IndexedLinesDistancer {
using LineType = ALineType;
using TreeType = ATreeType;
using VectorType = AVectorType;
using ScalarType = typename VectorType::Scalar;
const std::vector<LineType> &lines;
const TreeType &tree;
const VectorType origin;
inline VectorType closest_point_to_origin(size_t primitive_index,
ScalarType &squared_distance) {
VectorType nearest_point;
const LineType &line = lines[primitive_index];
squared_distance = line_alg::distance_to_squared(line, origin, &nearest_point);
return nearest_point;
}
};
}
// Build a balanced AABB Tree over a vector of float lines, balancing the tree
// on centroids of the lines.
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
// during tree traversal.
template<typename LineType>
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
const std::vector<LineType> &lines,
//FIXME do we want to apply an epsilon?
const float eps = 0)
{
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
using BoundingBox = typename TreeType::BoundingBox;
struct InputType {
size_t idx() const {
return m_idx;
}
const BoundingBox& bbox() const {
return m_bbox;
}
const VectorType& centroid() const {
return m_centroid;
}
size_t m_idx;
BoundingBox m_bbox;
VectorType m_centroid;
};
std::vector<InputType> input;
input.reserve(lines.size());
const VectorType veps(eps, eps);
for (size_t i = 0; i < lines.size(); ++i) {
const LineType &line = lines[i];
InputType n;
n.m_idx = i;
n.m_centroid = (line.a + line.b) * 0.5;
n.m_bbox = BoundingBox(line.a, line.a);
n.m_bbox.extend(line.b);
n.m_bbox.min() -= veps;
n.m_bbox.max() += veps;
input.emplace_back(n);
}
TreeType out;
out.build(std::move(input));
return out;
}
// Finding a closest line, its closest point and squared distance to the closest point
// Returns squared distance to the closest point or -1 if the input is empty.
template<typename LineType, typename TreeType, typename VectorType>
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
const std::vector<LineType> &lines,
const TreeType &tree,
const VectorType &point,
size_t &hit_idx_out,
Eigen::PlainObjectBase<VectorType> &hit_point_out)
{
using Scalar = typename VectorType::Scalar;
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>
{ lines, tree, point };
return tree.empty() ?
Scalar(-1) :
AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
}
}
}
#endif /* SRC_LIBSLIC3R_AABBTREELINES_HPP_ */

View file

@ -184,10 +184,10 @@ void AppConfig::set_defaults()
#ifdef _WIN32
#ifdef SUPPORT_3D_CONNEXION
//#ifdef SUPPORT_3D_CONNEXION
if (get("use_legacy_3DConnexion").empty())
set_bool("use_legacy_3DConnexion", false);
#endif
set_bool("use_legacy_3DConnexion", true);
//#endif
#ifdef SUPPORT_DARK_MODE
if (get("dark_color_mode").empty())

View file

@ -447,7 +447,7 @@ protected:
return std::make_tuple(score, fullbb);
}
std::function<double(const Item&)> get_objfn();
std::function<double(const Item&, const ItemGroup&)> get_objfn();
public:
AutoArranger(const TBin & bin,
@ -508,7 +508,8 @@ public:
bin_poly.contour.points.emplace_back(c0.x(), c1.y());
return bin_poly;
};
// preload fixed items (and excluded regions) on plate
m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
if (items.empty()) return;
@ -527,8 +528,12 @@ public:
}
}
cfg.object_function = [this, bb, starting_point](const Item& item) {
return fixed_overfit_topright_sliding(objfunc(item, starting_point), bb);
cfg.object_function = [this, bb, starting_point](const Item& item, const ItemGroup& packed_items) {
bool packed_are_excluded_region = std::all_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_virt_object && !itm.is_wipe_tower; });
if(packed_are_excluded_region)
return fixed_overfit_topright_sliding(objfunc(item, starting_point), bb);
else
return fixed_overfit(objfunc(item, starting_point), bb);
};
};
@ -597,11 +602,11 @@ public:
}
};
template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Box>::get_objfn()
{
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? m_bin.center() : m_bin.minCorner();
return [this, origin_pack](const Item &itm) {
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);
double score = std::get<0>(result);
@ -623,11 +628,11 @@ template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
};
}
template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Circle>::get_objfn()
{
auto bb = sl::boundingBox(m_bin);
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? bb.center() : bb.minCorner();
return [this, origin_pack](const Item &item) {
return [this, origin_pack](const Item &item, const ItemGroup&) {
auto result = objfunc(item, origin_pack);
@ -653,11 +658,11 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
// Specialization for a generalized polygon.
// Warning: this is much slower than with Box bed. Need further speedup.
template<>
std::function<double(const Item &)> AutoArranger<ExPolygon>::get_objfn()
std::function<double(const Item &, const ItemGroup&)> AutoArranger<ExPolygon>::get_objfn()
{
auto bb = sl::boundingBox(m_bin);
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? bb.center() : bb.minCorner();
return [this, origin_pack](const Item &itm) {
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);
double score = std::get<0>(result);

View file

@ -270,7 +270,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print
return diff_ex(brim_area, no_brim_area);
}
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
const float no_brim_offset, double& brim_width_max, std::map<ObjectID, double>& brim_width_map,
std::map<ObjectID, ExPolygons>& brimAreaMap,
@ -453,7 +453,7 @@ static ExPolygons inner_brim_area(const Print &print,
return diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area);
}
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
@ -875,7 +875,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
for (const auto& objectWithExtruder : objPrintVec)
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
std::map<ObjectID, ExPolygons> objectIslandMap;
ExPolygons objectIslands;
for (unsigned int extruderNo : printExtruders) {
++extruderNo;
@ -906,7 +906,11 @@ static ExPolygons outer_inner_brim_area(const Print& print,
std::vector<ModelVolume*> groupVolumePtrs;
for (auto& volumeID : volumeGroup.volume_ids) {
ModelVolume* currentModelVolumePtr = nullptr;
for (auto volumePtr : object->model_object()->volumes) {
//BBS: support shared object logic
const PrintObject* shared_object = object->get_shared_object();
if (!shared_object)
shared_object = object;
for (auto volumePtr : shared_object->model_object()->volumes) {
if (volumePtr->id() == volumeID) {
currentModelVolumePtr = volumePtr;
break;
@ -968,7 +972,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
append_and_translate(brim_area, brim_area_object, instance, print, brimAreaMap);
append_and_translate(no_brim_area, no_brim_area_object, instance);
append_and_translate(holes, holes_object, instance);
append_and_translate(objectIslandMap[instance.print_object->id()], objectIsland, instance);
append_and_translate(objectIslands, objectIsland, instance);
}
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
@ -1032,27 +1036,44 @@ static ExPolygons outer_inner_brim_area(const Print& print,
}
}
}
for (const PrintObject* object : print.objects()) {
for (const PrintObject* object : print.objects())
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
// BBS: brim should be contacted to at least one object island
if (objectIslandMap.find(object->id()) != objectIslandMap.end() && !objectIslandMap[object->id()].empty()) {
auto tempArea = brimAreaMap[object->id()];
brimAreaMap[object->id()].clear();
// the error bound is set to 2x flow width
for (auto& ta : tempArea) {
auto offsetedTa = offset_ex(ta, print.brim_flow().scaled_spacing() * 2, jtRound, SCALED_RESOLUTION);
if (!intersection_ex(offsetedTa, objectIslandMap[object->id()]).empty())
brimAreaMap[object->id()].push_back(ta);
}
}
}
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
}
//brim_area = diff_ex(brim_area, no_brim_area);
brim_area.clear();
for (const PrintObject* object : print.objects()) {
// BBS: brim should be contacted to at least one object's island or brim area
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
// find other objects' brim area
ExPolygons otherExPolys;
for (const PrintObject* otherObject : print.objects()) {
if ((otherObject->id() != object->id()) && (brimAreaMap.find(otherObject->id()) != brimAreaMap.end())) {
expolygons_append(otherExPolys, brimAreaMap[otherObject->id()]);
}
}
auto tempArea = brimAreaMap[object->id()];
brimAreaMap[object->id()].clear();
for (int ia = 0; ia != tempArea.size(); ++ia) {
// find this object's other brim area
ExPolygons otherExPoly;
for (int iao = 0; iao != tempArea.size(); ++iao)
if (iao != ia) otherExPoly.push_back(tempArea[iao]);
auto offsetedTa = offset_ex(tempArea[ia], print.brim_flow().scaled_spacing() * 2, jtRound, SCALED_RESOLUTION);
if (!intersection_ex(offsetedTa, objectIslands).empty() ||
!intersection_ex(offsetedTa, otherExPoly).empty() ||
!intersection_ex(offsetedTa, otherExPolys).empty())
brimAreaMap[object->id()].push_back(tempArea[ia]);
}
expolygons_append(brim_area, brimAreaMap[object->id()]);
}
}
return brim_area;
}
// Flip orientation of open polylines to minimize travel distance.
@ -1066,7 +1087,7 @@ static void optimize_polylines_by_reversing(Polylines *polylines)
double dist_to_start = (next.first_point() - prev.last_point()).cast<double>().norm();
double dist_to_end = (next.last_point() - prev.last_point()).cast<double>().norm();
if (dist_to_end < dist_to_start)
if (dist_to_end < dist_to_start)
next.reverse();
}
}

View file

@ -142,10 +142,12 @@ set(lisbslic3r_sources
GCodeWriter.hpp
Geometry.cpp
Geometry.hpp
Geometry/Bicubic.hpp
Geometry/Circle.cpp
Geometry/Circle.hpp
Geometry/ConvexHull.cpp
Geometry/ConvexHull.hpp
Geometry/Curves.hpp
Geometry/MedialAxis.cpp
Geometry/MedialAxis.hpp
Geometry/Voronoi.hpp
@ -176,6 +178,8 @@ set(lisbslic3r_sources
CustomGCode.hpp
Arrange.hpp
Arrange.cpp
NormalUtils.cpp
NormalUtils.hpp
Orient.hpp
Orient.cpp
MultiPoint.cpp
@ -222,6 +226,8 @@ set(lisbslic3r_sources
QuadricEdgeCollapse.cpp
QuadricEdgeCollapse.hpp
Semver.cpp
ShortEdgeCollapse.cpp
ShortEdgeCollapse.hpp
ShortestPath.cpp
ShortestPath.hpp
SLAPrint.cpp
@ -264,6 +270,8 @@ set(lisbslic3r_sources
Thread.hpp
TriangleSelector.cpp
TriangleSelector.hpp
TriangleSetSampling.cpp
TriangleSetSampling.hpp
MTUtils.hpp
VariableWidth.cpp
VariableWidth.hpp
@ -315,7 +323,6 @@ set(lisbslic3r_sources
SLA/Clustering.hpp
SLA/Clustering.cpp
SLA/ReprojectPointsOnMesh.hpp
Arachne/BeadingStrategy/BeadingStrategy.hpp
Arachne/BeadingStrategy/BeadingStrategy.cpp
Arachne/BeadingStrategy/BeadingStrategyFactory.hpp
@ -356,6 +363,8 @@ set(lisbslic3r_sources
Arachne/SkeletalTrapezoidationJoint.hpp
Arachne/WallToolPaths.hpp
Arachne/WallToolPaths.cpp
Shape/TextShape.hpp
Shape/TextShape.cpp
)
if (APPLE)

View file

@ -158,11 +158,10 @@ double ExtrusionLoop::length() const
return len;
}
bool ExtrusionLoop::split_at_vertex(const Point &point)
bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsilon)
{
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
int idx = path->polyline.find_point(point);
if (idx != -1) {
if (int idx = path->polyline.find_point(point, scaled_epsilon); idx != -1) {
if (this->paths.size() == 1) {
// just change the order of points
Polyline p1, p2;
@ -207,46 +206,57 @@ bool ExtrusionLoop::split_at_vertex(const Point &point)
return false;
}
std::pair<size_t, Point> ExtrusionLoop::get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const
ExtrusionLoop::ClosestPathPoint ExtrusionLoop::get_closest_path_and_point(const Point &point, bool prefer_non_overhang) const
{
// Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for.
size_t path_idx = 0;
Point p;
{
double min = std::numeric_limits<double>::max();
Point p_non_overhang;
size_t path_idx_non_overhang = 0;
double min_non_overhang = std::numeric_limits<double>::max();
for (const ExtrusionPath& path : this->paths) {
Point p_tmp = point.projection_onto(path.polyline);
double dist = (p_tmp - point).cast<double>().norm();
if (dist < min) {
p = p_tmp;
min = dist;
path_idx = &path - &this->paths.front();
}
if (prefer_non_overhang && !is_bridge(path.role()) && dist < min_non_overhang) {
p_non_overhang = p_tmp;
min_non_overhang = dist;
path_idx_non_overhang = &path - &this->paths.front();
}
ClosestPathPoint out{0, 0};
double min2 = std::numeric_limits<double>::max();
ClosestPathPoint best_non_overhang{0, 0};
double min2_non_overhang = std::numeric_limits<double>::max();
for (const ExtrusionPath &path : this->paths) {
std::pair<int, Point> foot_pt_ = foot_pt(path.polyline.points, point);
double d2 = (foot_pt_.second - point).cast<double>().squaredNorm();
if (d2 < min2) {
out.foot_pt = foot_pt_.second;
out.path_idx = &path - &this->paths.front();
out.segment_idx = foot_pt_.first;
min2 = d2;
}
if (prefer_non_overhang && min_non_overhang != std::numeric_limits<double>::max()) {
// Only apply the non-overhang point if there is one.
path_idx = path_idx_non_overhang;
p = p_non_overhang;
if (prefer_non_overhang && !is_bridge(path.role()) && d2 < min2_non_overhang) {
best_non_overhang.foot_pt = foot_pt_.second;
best_non_overhang.path_idx = &path - &this->paths.front();
best_non_overhang.segment_idx = foot_pt_.first;
min2_non_overhang = d2;
}
}
return std::make_pair(path_idx, p);
if (prefer_non_overhang && min2_non_overhang != std::numeric_limits<double>::max())
// Only apply the non-overhang point if there is one.
out = best_non_overhang;
return out;
}
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon)
{
if (this->paths.empty())
return;
auto [path_idx, p] = get_closest_path_and_point(point, prefer_non_overhang);
auto [path_idx, segment_idx, p] = get_closest_path_and_point(point, prefer_non_overhang);
// Snap p to start or end of segment_idx if closer than scaled_epsilon.
{
const Point *p1 = this->paths[path_idx].polyline.points.data() + segment_idx;
const Point *p2 = p1;
++p2;
double d2_1 = (point - *p1).cast<double>().squaredNorm();
double d2_2 = (point - *p2).cast<double>().squaredNorm();
const double thr2 = scaled_epsilon * scaled_epsilon;
if (d2_1 < d2_2) {
if (d2_1 < thr2) p = *p1;
} else {
if (d2_2 < thr2) p = *p2;
}
}
// now split path_idx in two parts
const ExtrusionPath &path = this->paths[path_idx];

View file

@ -314,9 +314,15 @@ public:
const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
Polygon polygon() const;
double length() const override;
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang);
std::pair<size_t, Point> get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const;
bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled<double>(0.001));
void split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon = scaled<double>(0.001));
struct ClosestPathPoint
{
size_t path_idx;
size_t segment_idx;
Point foot_pt;
};
ClosestPathPoint get_closest_path_and_point(const Point &point, bool prefer_non_overhang) const;
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.

View file

@ -31,9 +31,6 @@
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
const int LOAD_STEP_STAGE_READ_FILE = 0;
const int LOAD_STEP_STAGE_GET_SOLID = 1;
const int LOAD_STEP_STAGE_GET_MESH = 2;
namespace Slic3r {
@ -213,11 +210,11 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
}
}
bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepIsUtf8Fn isUtf8Fn)
bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
{
bool cb_cancel = false;
if (proFn) {
proFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
if (stepFn) {
stepFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
if (cb_cancel)
return false;
}
@ -245,9 +242,13 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepI
unsigned int id{1};
Standard_Integer topShapeLength = topLevelShapes.Length() + 1;
auto stage_unit2 = topShapeLength / LOAD_STEP_STAGE_UNIT_NUM + 1;
for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) {
if (proFn) {
proFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
if (stepFn) {
if ((iLabel % stage_unit2) == 0) {
stepFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
}
if (cb_cancel) {
shapeTool.reset(nullptr);
application->Close(document);
@ -257,14 +258,94 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepI
getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids);
}
ModelObject* new_object = model->add_object();
const char *last_slash = strrchr(path, DIR_SEPARATOR);
std::vector<stl_file> stl;
stl.resize(namedSolids.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, namedSolids.size()), [&](const tbb::blocked_range<size_t> &range) {
for (size_t i = range.begin(); i < range.end(); i++) {
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
// BBS: calculate total number of the nodes and triangles
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
if (aNbTriangles == 0)
// BBS: No triangulation on the shape.
continue;
stl[i].stats.type = inmemory;
stl[i].stats.number_of_facets = (uint32_t) aNbTriangles;
stl[i].stats.original_num_facets = stl[i].stats.number_of_facets;
stl_allocate(&stl[i]);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
// BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
// BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape &aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
// BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
// BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
Standard_Integer anId[3];
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED)
std::swap(anId[1], anId[2]);
// BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] + aNodeOffset - 1].cast<float>();
facet.vertex[1] = points[anId[1] + aNodeOffset - 1].cast<float>();
facet.vertex[2] = points[anId[2] + aNodeOffset - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl[i].facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
}
});
ModelObject *new_object = model->add_object();
const char * last_slash = strrchr(path, DIR_SEPARATOR);
new_object->name.assign((last_slash == nullptr) ? path : last_slash + 1);
new_object->input_file = path;
for (size_t i = 0; i < namedSolids.size(); ++i) {
if (proFn) {
proFn(LOAD_STEP_STAGE_GET_MESH, i, namedSolids.size(), cb_cancel);
auto stage_unit3 = stl.size() / LOAD_STEP_STAGE_UNIT_NUM + 1;
for (size_t i = 0; i < stl.size(); i++) {
if (stepFn) {
if ((i % stage_unit3) == 0) {
stepFn(LOAD_STEP_STAGE_GET_MESH, i, stl.size(), cb_cancel);
}
if (cb_cancel) {
model->delete_object(new_object);
shapeTool.reset(nullptr);
@ -273,94 +354,13 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepI
}
}
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
//BBS: calculate total number of the nodes and triangles
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
if (aNbTriangles == 0) {
//BBS: No triangulation on the shape.
continue;
}
stl_file stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)aNbTriangles;
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
//BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
//BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape& aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
//BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
//BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
Standard_Integer anId[3];
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED) {
//BBS: swap 1, 2.
Standard_Integer aTmpIdx = anId[1];
anId[1] = anId[2];
anId[2] = aTmpIdx;
}
//BBS: Update nodes according to the offset.
anId[0] += aNodeOffset;
anId[1] += aNodeOffset;
anId[2] += aNodeOffset;
//BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] - 1].cast<float>();
facet.vertex[1] = points[anId[1] - 1].cast<float>();
facet.vertex[2] = points[anId[2] - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
TriangleMesh triangle_mesh;
triangle_mesh.from_stl(stl);
ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
triangle_mesh.from_stl(stl[i]);
ModelVolume *new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int)model->objects.size() - 1;
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
new_volume->source.object_idx = (int) model->objects.size() - 1;
new_volume->source.volume_idx = (int) new_object->volumes.size() - 1;
}
shapeTool.reset(nullptr);

View file

@ -6,6 +6,13 @@ namespace Slic3r {
class TriangleMesh;
class ModelObject;
// load step stage
const int LOAD_STEP_STAGE_READ_FILE = 0;
const int LOAD_STEP_STAGE_GET_SOLID = 1;
const int LOAD_STEP_STAGE_GET_MESH = 2;
const int LOAD_STEP_STAGE_NUM = 3;
const int LOAD_STEP_STAGE_UNIT_NUM = 5;
typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;

View file

@ -14,10 +14,10 @@
namespace Slic3r {
bool load_stl(const char *path, Model *model, const char *object_name_in)
bool load_stl(const char *path, Model *model, const char *object_name_in, ImportstlProgressFn stlFn)
{
TriangleMesh mesh;
if (! mesh.ReadSTLFile(path)) {
if (! mesh.ReadSTLFile(path, true, stlFn)) {
// die "Failed to open $file\n" if !-e $path;
return false;
}

View file

@ -1,13 +1,16 @@
#ifndef slic3r_Format_STL_hpp_
#define slic3r_Format_STL_hpp_
#include <admesh/stl.h>
namespace Slic3r {
class Model;
class TriangleMesh;
class ModelObject;
// Load an STL file into a provided model.
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr);
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr, ImportstlProgressFn stlFn = nullptr);
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
extern bool store_stl(const char *path, ModelObject *model_object, bool binary);

View file

@ -258,6 +258,8 @@ static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
static constexpr const char* MESH_SHARED_KEY = "mesh_shared";
static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
@ -702,6 +704,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string m_thumbnail_path;
std::vector<std::string> m_sub_model_paths;
std::map<int, ModelVolume*> m_shared_meshes;
//BBS: plater related structures
bool m_is_bbl_3mf { false };
bool m_parsing_slice_info { false };
@ -847,8 +851,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _handle_start_relationship(const char** attributes, unsigned int num_attributes);
void _generate_current_object_list(std::vector<Id> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects);
bool _generate_volumes_new(ModelObject& object, const std::vector<Id> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
void _generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects);
bool _generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
// callbacks to parse the .model file
@ -1281,7 +1285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
add_error("3rd 3mf, can not find object, id " + std::to_string(object.first.second));
return false;
}
std::vector<Id> object_id_list;
std::vector<Component> object_id_list;
_generate_current_object_list(object_id_list, object.first, m_current_objects);
ObjectMetadata::VolumeMetadataList volumes;
@ -1289,7 +1293,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
for (int k = 0; k < object_id_list.size(); k++)
{
Id object_id = object_id_list[k];
Id object_id = object_id_list[k].object_id;
volumes.emplace_back(object_id.second);
}
@ -1344,7 +1348,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
model_object->sla_drain_holes = std::move(obj_drain_holes->second);
}*/
std::vector<Id> object_id_list;
std::vector<Component> object_id_list;
_generate_current_object_list(object_id_list, object.first, m_current_objects);
ObjectMetadata::VolumeMetadataList volumes;
@ -1375,7 +1379,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
for (int k = 0; k < object_id_list.size(); k++)
{
Id object_id = object_id_list[k];
Id object_id = object_id_list[k].object_id;
volumes.emplace_back(object_id.second);
}
@ -2757,31 +2761,31 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
_(L("The selected 3MF contains multi-material painted object using a newer version of BambuStudio and is not compatible.")));*/
} else if (m_curr_metadata_name == BBL_MODEL_ID_TAG) {
m_model_id = m_curr_characters;
m_model_id = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_MODEL_NAME_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found model name = " << m_curr_characters;
model_info.model_name = m_curr_characters;
model_info.model_name = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESIGNER_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer = " << m_curr_characters;
m_designer = m_curr_characters;
m_designer = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESIGNER_USER_ID_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer_user_id = " << m_curr_characters;
m_designer_user_id = m_curr_characters;
m_designer_user_id = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESIGNER_COVER_FILE_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer_cover = " << m_curr_characters;
model_info.cover_file = m_curr_characters;
model_info.cover_file = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESCRIPTION_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found description = " << m_curr_characters;
model_info.description = m_curr_characters;
model_info.description = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_LICENSE_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found license = " << m_curr_characters;
model_info.license = m_curr_characters;
model_info.license = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_COPYRIGHT_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found copyright = " << m_curr_characters;
model_info.copyright = m_curr_characters;
model_info.copyright = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_REGION_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found region = " << m_curr_characters;
m_contry_code = m_curr_characters;
m_contry_code = xml_unescape(m_curr_characters);
}
return true;
@ -3279,34 +3283,34 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true;
}
void _BBS_3MF_Importer::_generate_current_object_list(std::vector<Id> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects)
void _BBS_3MF_Importer::_generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects)
{
std::list<Id> id_list;
id_list.push_back(object_id);
std::list<Component> id_list;
id_list.push_back({ object_id, Transform3d::Identity() });
while (!id_list.empty())
{
Id current_id = id_list.front();
Component current_id = id_list.front();
id_list.pop_front();
IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id);
IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id.object_id);
if (current_object != current_objects.end()) {
//found one
if (!current_object->second.components.empty()) {
for (const Component& comp: current_object->second.components)
{
id_list.push_back(comp.object_id);
id_list.push_back(comp);
}
}
else if (!(current_object->second.geometry.empty())) {
//CurrentObject* ptr = &(current_objects[current_id]);
//CurrentObject* ptr2 = &(current_object->second);
sub_objects.push_back(current_object->first);
sub_objects.push_back({ current_object->first, current_id.transform });
}
}
}
}
bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Id> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
{
if (!object.volumes.empty()) {
add_error("object already built with parts");
@ -3319,7 +3323,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
for (unsigned int index = 0; index < sub_objects.size(); index++)
{
//find the volume metadata firstly
Id object_id = sub_objects[index];
Component sub_comp = sub_objects[index];
Id object_id = sub_comp.object_id;
IdToCurrentObjectMap::iterator current_object = m_current_objects.find(object_id);
if (current_object == m_current_objects.end()) {
add_error("sub_objects can not be found, id=" + std::to_string(object_id.second));
@ -3338,70 +3343,114 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
int shared_mesh_id = -1;
if (volume_data)
{
int found_count = 0;
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data->metadata) {
if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
break;
found_count++;
}
else if (metadata.key == MESH_SHARED_KEY){
//add the shared mesh logic
shared_mesh_id = ::atoi(metadata.value.c_str());
found_count++;
}
if (found_count >= 2)
break;
}
}
else {
//create a volume_data
volume_data = &default_volume_data;
}
// splits volume out of imported geometry
indexed_triangle_set its;
its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end());
const size_t triangles_count = its.indices.size();
ModelVolume* volume = nullptr;
ModelVolume *shared_volume = nullptr;
if (shared_mesh_id != -1) {
std::map<int, ModelVolume*>::iterator iter = m_shared_meshes.find(shared_mesh_id);
if (iter != m_shared_meshes.end()) {
shared_volume = iter->second;
}
}
const size_t triangles_count = sub_object->geometry.triangles.size();
if (triangles_count == 0) {
add_error("found no trianges in the object " + std::to_string(sub_object->id));
return false;
}
for (const Vec3i& face : its.indices) {
for (const int tri_id : face) {
if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) {
add_error("invalid vertex id in object " + std::to_string(sub_object->id));
return false;
if (!shared_volume){
// splits volume out of imported geometry
indexed_triangle_set its;
its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end());
//const size_t triangles_count = its.indices.size();
//if (triangles_count == 0) {
// add_error("found no trianges in the object " + std::to_string(sub_object->id));
// return false;
//}
for (const Vec3i& face : its.indices) {
for (const int tri_id : face) {
if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) {
add_error("invalid vertex id in object " + std::to_string(sub_object->id));
return false;
}
}
}
}
its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end());
its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end());
// BBS
for (const std::string prop_str : sub_object->geometry.face_properties) {
FaceProperty face_prop;
face_prop.from_string(prop_str);
its.properties.push_back(face_prop);
}
TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats);
if (m_version == 0) {
// if the 3mf was not produced by BambuStudio and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
//FIXME do the mesh fixing?
// BBS
for (const std::string prop_str : sub_object->geometry.face_properties) {
FaceProperty face_prop;
face_prop.from_string(prop_str);
its.properties.push_back(face_prop);
}
}
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats);
if (m_version == 0) {
// if the 3mf was not produced by BambuStudio and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
//FIXME do the mesh fixing?
}
}
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
volume = object.add_volume(std::move(triangle_mesh));
m_shared_meshes[sub_object->id] = volume;
}
else {
//create volume to use shared mesh
volume = object.add_volume_with_shared_mesh(*shared_volume);
}
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
volume->calculate_convex_hull();
//set transform from 3mf
Slic3r::Geometry::Transformation comp_transformatino(sub_comp.transform);
volume->set_transformation(volume->get_transformation() * comp_transformatino);
if (shared_volume) {
const TriangleMesh& trangle_mesh = volume->mesh();
Vec3d shift = trangle_mesh.get_init_shift();
if (!shift.isApprox(Vec3d::Zero()))
volume->translate(shift);
}
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
if (m_load_config) {
{
volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count);
@ -3448,7 +3497,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
volume->source.is_converted_from_inches = metadata.value == "1";
else if (metadata.key == SOURCE_IN_METERS)
volume->source.is_converted_from_meters = metadata.value == "1";
else if (metadata.key == MATRIX_KEY)
else if ((metadata.key == MATRIX_KEY) || (metadata.key == MESH_SHARED_KEY))
continue;
else
volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
@ -3814,18 +3863,38 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
auto filename = boost::format("3D/Objects/%s_%d.model") % object.name % obj_id;
std::string filepath = temp_path + "/" + filename.str();
if (!open_zip_writer(&archive, filepath)) {
std::string filepath_tmp = filepath + ".tmp";
boost::system::error_code ec;
boost::filesystem::remove(filepath_tmp, ec);
if (!open_zip_writer(&archive, filepath_tmp)) {
add_error("Unable to open the file");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n");
return false;
}
struct close_lock
{
mz_zip_archive & archive;
std::string const * filename;
void close() {
close_zip_writer(&archive);
filename = nullptr;
}
~close_lock() {
if (filename) {
close_zip_writer(&archive);
boost::filesystem::remove(*filename);
}
}
} lock{archive, &filepath_tmp};
IdToObjectDataMap objects_data;
objects_data.insert({obj_id, {&object, obj_id}});
_add_model_file_to_archive(filename.str(), archive, model, objects_data);
mz_zip_writer_finalize_archive(&archive);
close_zip_writer(&archive);
lock.close();
boost::filesystem::rename(filepath_tmp, filepath, ec);
return true;
}
@ -4363,6 +4432,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false;
}
{
std::stringstream stream;
reset_stream(stream);
@ -4413,27 +4483,29 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
model_id = project->project_model_id;
region_code = project->project_country_code;
}
if (!sub_model) {
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_NAME_TAG << "\">" << xml_escape(name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_TAG << "\">" << xml_escape(user_name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_USER_ID_TAG << "\">" << user_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_COVER_FILE_TAG << "\">" << xml_escape(design_cover) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESCRIPTION_TAG << "\">" << xml_escape(description) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_COPYRIGHT_TAG << "\">" << xml_escape(copyright) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_LICENSE_TAG << "\">" << xml_escape(license) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_NAME_TAG << "\">" << xml_escape(name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_TAG << "\">" << xml_escape(user_name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_USER_ID_TAG << "\">" << user_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_COVER_FILE_TAG << "\">" << xml_escape(design_cover) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESCRIPTION_TAG << "\">" << xml_escape(description) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_COPYRIGHT_TAG << "\">" << xml_escape(copyright) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_LICENSE_TAG << "\">" << xml_escape(license) << "</" << METADATA_TAG << ">\n";
/* save model info */
if (!model_id.empty()) {
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_ID_TAG << "\">" << model_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_REGION_TAG << "\">" << region_code << "</" << METADATA_TAG << ">\n";
}
/* save model info */
if (!model_id.empty()) {
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_ID_TAG << "\">" << model_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_REGION_TAG << "\">" << region_code << "</" << METADATA_TAG << ">\n";
std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
// keep only the date part of the string
date = date.substr(0, 10);
stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
}
std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
// keep only the date part of the string
date = date.substr(0, 10);
stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
stream << " <" << RESOURCES_TAG << ">\n";
std::string buf = stream.str();
if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
@ -4645,12 +4717,37 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (m_from_backup_save) {
for (unsigned int index = 1; index <= object.volumes.size(); index ++) {
unsigned int ref_id = object_id | (index << 16);
stream << " <" << COMPONENT_TAG << " objectid=\"" << ref_id << "\"/>\n";
stream << " <" << COMPONENT_TAG << " objectid=\"" << ref_id; // << "\"/>\n";
//add the transform of the volume
ModelVolume* volume = object.volumes[index - 1];
const Transform3d& transf = volume->get_matrix();
stream << "\" " << TRANSFORM_ATTR << "=\"";
for (unsigned c = 0; c < 4; ++c) {
for (unsigned r = 0; r < 3; ++r) {
stream << transf(r, c);
if (r != 2 || c != 3)
stream << " ";
}
}
stream << "\"/>\n";
}
}
else {
for (unsigned int index = object_id; index < volume_start_id; index ++)
stream << " <" << COMPONENT_TAG << " objectid=\"" << index << "\"/>\n";
for (unsigned int index = object_id; index < volume_start_id; index ++) {
stream << " <" << COMPONENT_TAG << " objectid=\"" << index; // << "\"/>\n";
//add the transform of the volume
ModelVolume* volume = object.volumes[index - object_id];
const Transform3d& transf = volume->get_matrix();
stream << "\" " << TRANSFORM_ATTR << "=\"";
for (unsigned c = 0; c < 4; ++c) {
for (unsigned r = 0; r < 3; ++r) {
stream << transf(r, c);
if (r != 2 || c != 3)
stream << " ";
}
}
stream << "\"/>\n";
}
}
}
else {
@ -4800,7 +4897,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {
Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
//don't save the volume's matrix into vertex data
//add the shared mesh logic
//Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
Vec3f v = its.vertices[i];
char* ptr = buf;
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\"");
ptr = format_coordinate(v.x(), ptr);
@ -5202,6 +5302,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _BBS_3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx, bool save_gcode)
{
std::stringstream stream;
std::map<const TriangleMesh*, int> shared_meshes;
// Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
// when loaded as accurately as possible.
stream << std::setprecision(std::numeric_limits<double>::max_digits10);
@ -5294,6 +5395,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
//add the shared mesh logic
const TriangleMesh* current_mesh = volume->mesh_ptr();
std::map<const TriangleMesh*,int>::iterator mesh_iter;
mesh_iter = shared_meshes.find(current_mesh);
if (mesh_iter != shared_meshes.end()) {
stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << MESH_SHARED_KEY << "\" " << VALUE_ATTR << "=\"" << mesh_iter->second << "\"/>\n";
}
else {
shared_meshes[current_mesh] = it->second;
}
// stores mesh's statistics
const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
stream << " <" << MESH_STAT_TAG << " ";

View file

@ -166,6 +166,7 @@ const int IMPORT_STAGE_CHECK_MODE_GCODE = 9;
const int UPDATE_GCODE_RESULT = 10;
const int IMPORT_LOAD_CONFIG = 11;
const int IMPORT_LOAD_MODEL_OBJECTS = 12;
const int IMPORT_STAGE_MAX = 13;
//BBS export 3mf progress
typedef std::function<void(int export_stage, int current, int total, bool& cancel)> Export3mfProgressFn;

View file

@ -554,6 +554,9 @@ bool GCode::gcode_label_objects = false;
{
std::string gcode;
assert(m_layer_idx >= 0);
if (m_layer_idx >= (int) m_tool_changes.size()) return gcode;
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
// resulting in a wipe tower with sparse layers.
double wipe_tower_z = -1;
@ -571,16 +574,12 @@ bool GCode::gcode_label_objects = false;
m_is_first_print = false;
}
assert(m_layer_idx >= 0);
if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
if (m_layer_idx < (int)m_tool_changes.size()) {
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer.");
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer.");
if (!ignore_sparse) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z);
m_last_wipe_tower_print_z = wipe_tower_z;
}
if (!ignore_sparse) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z);
m_last_wipe_tower_print_z = wipe_tower_z;
}
}
@ -667,6 +666,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
--idx_tree_support_layer;
}
layer_to_print.original_object = &object;
layers_to_print.push_back(layer_to_print);
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
@ -1261,6 +1261,7 @@ enum BambuBedType {
bbtCoolPlate = 1,
bbtEngineeringPlate = 2,
bbtHighTemperaturePlate = 3,
bbtTexturedPEIPlate = 4,
};
static BambuBedType to_bambu_bed_type(BedType type)
@ -1272,6 +1273,8 @@ static BambuBedType to_bambu_bed_type(BedType type)
bambu_bed_type = bbtEngineeringPlate;
else if (type == btPEI)
bambu_bed_type = bbtHighTemperaturePlate;
else if (type == btPTE)
bambu_bed_type = bbtTexturedPEIPlate;
return bambu_bed_type;
}
@ -1570,7 +1573,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
print.throw_if_canceled();
// Collect custom seam data from all objects.
m_seam_placer.init(print);
std::function<void(void)> throw_if_canceled_func = [&print]() { print.throw_if_canceled(); };
m_seam_placer.init(print, throw_if_canceled_func);
// BBS: priming logic is removed, always set first extruer here.
//if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
@ -2153,7 +2157,9 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
// Sequential print, single object is being printed.
for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
const PrintObject *print_object = layers[layer_id].object();
//BBS:add the support of shared print object
const PrintObject *print_object = layers[layer_id].original_object;
//const PrintObject *print_object = layers[layer_id].object();
if (print_object)
out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx);
}
@ -2163,7 +2169,9 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
sorted.reserve(objects_by_extruder.size());
for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
const PrintObject *print_object = layers[layer_id].object();
//BBS:add the support of shared print object
const PrintObject *print_object = layers[layer_id].original_object;
//const PrintObject *print_object = layers[layer_id].object();
if (print_object)
sorted.emplace_back(print_object, &object_by_extruder);
}
@ -2173,6 +2181,10 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
out.reserve(sorted.size());
for (const PrintInstance *instance : *ordering) {
const PrintObject &print_object = *instance->print_object;
//BBS:add the support of shared print object
//const PrintObject* print_obj_ptr = &print_object;
//if (print_object.get_shared_object())
// print_obj_ptr = print_object.get_shared_object();
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
if (it != sorted.end() && it->first == &print_object)
@ -2795,7 +2807,6 @@ GCode::LayerResult GCode::process_layer(
m_wipe_tower->set_is_first_print(true);
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
{
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
@ -2910,12 +2921,7 @@ GCode::LayerResult GCode::process_layer(
m_layer = layers[instance_to_print.layer_id].tree_support_layer;
}
m_object_layer_over_raft = false;
// BBS. Keep paths order
#if 0
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, erSupportTransition, erSupportMaterialInterface or erMixed for all extrusion paths.
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
#else
//BBS: print supports' brims first
if (this->m_objSupportsWithBrim.find(instance_to_print.print_object.id()) != this->m_objSupportsWithBrim.end() && !print_wipe_extrusions) {
this->set_origin(0., 0.);
@ -2945,12 +2951,10 @@ GCode::LayerResult GCode::process_layer(
ExtrusionRole support_extrusion_role = instance_to_print.object_by_extruder.support_extrusion_role;
bool is_overridden = support_extrusion_role == erSupportMaterialInterface ? support_intf_overridden : support_overridden;
if (is_overridden == (print_wipe_extrusions != 0))
support_eec.entities = filter_by_extrusion_role(instance_to_print.object_by_extruder.support->entities, instance_to_print.object_by_extruder.support_extrusion_role);
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, erSupportTransition, erSupportMaterialInterface or erMixed for all extrusion paths.
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, support_extrusion_role));
for (auto& ptr : support_eec.entities)
ptr = ptr->clone();
gcode += this->extrude_support(support_eec);
#endif
m_layer = layer_to_print.layer();
m_object_layer_over_raft = object_layer_over_raft;
}
@ -2984,9 +2988,9 @@ GCode::LayerResult GCode::process_layer(
//This behaviour is same with cura
if (is_infill_first && !first_layer) {
gcode += this->extrude_infill(print, by_region_specific, false);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
gcode += this->extrude_perimeters(print, by_region_specific);
} else {
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
gcode += this->extrude_perimeters(print, by_region_specific);
gcode += this->extrude_infill(print,by_region_specific, false);
}
// ironing
@ -3164,14 +3168,11 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
}
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed)
{
// get a copy; don't modify the orientation of the original loop object otherwise
// next copies (if any) would not detect the correct orientation
if (m_layer->lower_layer && lower_layer_edge_grid != nullptr && ! *lower_layer_edge_grid)
*lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
//BBS: extrude contour of wall ccw, hole of wall cw, except spiral mode
bool was_clockwise = loop.is_clockwise();
if (m_config.spiral_mode || !is_perimeter(loop.role()))
@ -3181,17 +3182,13 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// find the point of the loop that is closest to the current extruder position
// or randomize if requested
Point last_pos = this->last_pos();
if (m_config.spiral_mode) {
if (!m_config.spiral_mode && description == "perimeter") {
assert(m_layer != nullptr);
bool is_outer_wall_first = m_config.wall_infill_order == WallInfillOrder::OuterInnerInfill
|| m_config.wall_infill_order == WallInfillOrder::InfillOuterInner;
m_seam_placer.place_seam(m_layer, loop, is_outer_wall_first, this->last_pos());
} else
loop.split_at(last_pos, false);
}
else {
//BBS
bool is_outer_wall_first =
m_config.wall_infill_order == WallInfillOrder::OuterInnerInfill ||
m_config.wall_infill_order == WallInfillOrder::InfillOuterInner;
m_seam_placer.place_seam(loop, this->last_pos(), is_outer_wall_first,
EXTRUDER_CONFIG(nozzle_diameter), lower_layer_edge_grid ? lower_layer_edge_grid->get() : nullptr);
}
// clip the path to avoid the extruder to get exactly on the first point of the loop;
// if polyline was shorter than the clipping distance we'd get a null polyline, so
@ -3301,14 +3298,14 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
return gcode;
}
std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string description, double speed)
{
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
return this->extrude_path(*path, description, speed);
else if (const ExtrusionMultiPath* multipath = dynamic_cast<const ExtrusionMultiPath*>(&entity))
return this->extrude_multi_path(*multipath, description, speed);
else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid);
return this->extrude_loop(*loop, description, speed);
else
throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()");
return "";
@ -3330,24 +3327,15 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou
}
// Extrude perimeters: Decide where to put seams (hide or align seams).
std::string GCode::extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid)
std::string GCode::extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region)
{
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region)
if (! region.perimeters.empty()) {
m_config.apply(print.get_print_region(&region - &by_region.front()).config());
// plan_perimeters tries to place seams, it needs to have the lower_layer_edge_grid calculated already.
if (m_layer->lower_layer && ! lower_layer_edge_grid)
lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
m_seam_placer.plan_perimeters(std::vector<const ExtrusionEntity*>(region.perimeters.begin(), region.perimeters.end()),
*m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter),
(m_layer == NULL ? nullptr : m_layer->object()),
(lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr));
for (const ExtrusionEntity* ee : region.perimeters)
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
gcode += this->extrude_entity(*ee, "perimeter", -1.);
}
return gcode;
}
@ -3817,6 +3805,11 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
return false;
}
//BBS: force to retract when leave from external perimeter for a long travel
//Better way is judging whether the travel move direction is same with last extrusion move.
if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter)
return true;
if (role == erSupportMaterial || role == erSupportTransition) {
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
//FIXME support_layer->support_islands.contains should use some search structure!

View file

@ -41,11 +41,11 @@ class OozePrevention {
public:
bool enable;
Points standby_points;
OozePrevention() : enable(false) {}
std::string pre_toolchange(GCode &gcodegen);
std::string post_toolchange(GCode &gcodegen);
private:
int _get_temp(GCode &gcodegen);
};
@ -54,7 +54,7 @@ class Wipe {
public:
bool enable;
Polyline path;
Wipe() : enable(false) {}
bool has_path() const { return !this->path.points.empty(); }
void reset_path() { this->path = Polyline(); }
@ -136,15 +136,15 @@ public:
};
class GCode {
public:
public:
GCode() :
m_origin(Vec2d::Zero()),
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_extrusion_role_markers(false),
m_last_processor_extrusion_role(erNone),
m_layer_count(0),
m_layer_index(-1),
m_layer_index(-1),
m_layer(nullptr),
m_object_layer_over_raft(false),
//m_volumetric_speed(0),
@ -201,10 +201,11 @@ public:
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr) {}
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr), original_object(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const TreeSupportLayer* tree_support_layer;
const PrintObject* original_object; //BBS: used for shared object logic
const Layer* layer() const
{
if (object_layer != nullptr)
@ -218,7 +219,11 @@ public:
return nullptr;
}
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
const PrintObject* object() const
{
return (this->layer() != nullptr) ? this->layer()->object() : nullptr;
}
coordf_t print_z() const
{
coordf_t sum_z = 0.;
@ -249,7 +254,7 @@ private:
bool is_open() const { return f; }
bool is_error() const;
void flush();
void close();
@ -257,12 +262,12 @@ private:
void write(const std::string& what) { this->write(what.c_str()); }
void write(const char* what);
// Write a string into a file.
// Write a string into a file.
// Add a newline, if the string does not end with a newline already.
// Used to export a custom G-code section processed by the PlaceholderParser.
void writeln(const std::string& what);
// Formats and write into a file the given data.
// Formats and write into a file the given data.
void write_format(const char* format, ...);
private:
@ -325,8 +330,8 @@ private:
std::string preamble();
// BBS
std::string change_layer(coordf_t print_z, bool lazy_raise = false);
std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1.);
std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1.);
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
@ -375,7 +380,7 @@ private:
InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) :
object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {}
// Repository
// Repository
ObjectByExtruder &object_by_extruder;
// Index into std::vector<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const size_t layer_id;
@ -393,7 +398,7 @@ private:
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx);
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing);
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
@ -500,14 +505,14 @@ private:
bool object_layer_over_raft() const { return m_object_layer_over_raft; }
friend ObjectByExtruder& object_by_extruder(
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
size_t num_objects);
friend std::vector<ObjectByExtruder::Island>& object_islands_by_extruder(
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
size_t num_objects,
size_t num_islands);

File diff suppressed because it is too large Load diff

View file

@ -3,12 +3,16 @@
#include <optional>
#include <vector>
#include <memory>
#include <atomic>
#include "libslic3r/libslic3r.h"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/AABBTreeIndirect.hpp"
#include "libslic3r/KDTreeIndirect.hpp"
namespace Slic3r {
@ -16,119 +20,147 @@ class PrintObject;
class ExtrusionLoop;
class Print;
class Layer;
namespace EdgeGrid { class Grid; }
class SeamHistory {
public:
SeamHistory() { clear(); }
std::optional<Point> get_last_seam(const PrintObject* po, size_t layer_id, const BoundingBox& island_bb);
void add_seam(const PrintObject* po, const Point& pos, const BoundingBox& island_bb);
void clear();
private:
struct SeamPoint {
Point m_pos;
BoundingBox m_island_bb;
};
std::map<const PrintObject*, std::vector<SeamPoint>> m_data_last_layer;
std::map<const PrintObject*, std::vector<SeamPoint>> m_data_this_layer;
size_t m_layer_id;
};
class SeamPlacer {
public:
void init(const Print& print);
// When perimeters are printed, first call this function with the respective
// external perimeter. SeamPlacer will find a location for its seam and remember it.
// Subsequent calls to get_seam will return this position.
void plan_perimeters(const std::vector<const ExtrusionEntity*> perimeters,
const Layer& layer, SeamPosition seam_position,
Point last_pos, coordf_t nozzle_dmr, const PrintObject* po,
const EdgeGrid::Grid* lower_layer_edge_grid);
void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter,
const EdgeGrid::Grid* lower_layer_edge_grid);
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
private:
// When given an external perimeter (!), returns the seam.
Point calculate_seam(const Layer& layer, const SeamPosition seam_position,
const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po,
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos);
struct CustomTrianglesPerLayer {
Polygons polys;
TreeType tree;
};
// Just a cache to save some lookups.
const Layer* m_last_layer_po = nullptr;
coordf_t m_last_print_z = -1.;
const PrintObject* m_last_po = nullptr;
struct SeamPoint {
Point pt;
bool precalculated = false;
bool external = false;
const Layer* layer = nullptr;
SeamPosition seam_position;
const PrintObject* po = nullptr;
};
std::vector<SeamPoint> m_plan;
size_t m_plan_idx;
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
std::vector<const PrintObject*> m_po_list;
//std::map<const PrintObject*, Point> m_last_seam_position;
SeamHistory m_seam_history;
// Get indices of points inside enforcers and blockers.
void get_enforcers_and_blockers(size_t layer_id,
const Polygon& polygon,
size_t po_id,
std::vector<size_t>& enforcers_idxs,
std::vector<size_t>& blockers_idxs) const;
// Apply penalties to points inside enforcers/blockers.
void apply_custom_seam(const Polygon& polygon, size_t po_id,
std::vector<float>& penalties,
const std::vector<float>& lengths,
int layer_id, SeamPosition seam_position) const;
// Return random point of a polygon. The distribution will be uniform
// along the contour and account for enforcers and blockers.
Point get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_id,
bool* saw_custom = nullptr) const;
// Is there any enforcer/blocker on this layer?
bool is_custom_seam_on_layer(size_t layer_id, size_t po_idx) const {
return is_custom_enforcer_on_layer(layer_id, po_idx)
|| is_custom_blocker_on_layer(layer_id, po_idx);
}
bool is_custom_enforcer_on_layer(size_t layer_id, size_t po_idx) const {
return (! m_enforcers.at(po_idx).empty() && ! m_enforcers.at(po_idx)[layer_id].polys.empty());
}
bool is_custom_blocker_on_layer(size_t layer_id, size_t po_idx) const {
return (! m_blockers.at(po_idx).empty() && ! m_blockers.at(po_idx)[layer_id].polys.empty());
}
};
namespace EdgeGrid {
class Grid;
}
namespace SeamPlacerImpl {
// ************ FOR BACKPORT COMPATIBILITY ONLY ***************
// Angle from v1 to v2, returning double atan2(y, x) normalized to <-PI, PI>.
template<typename Derived, typename Derived2> inline double angle(const Eigen::MatrixBase<Derived> &v1, const Eigen::MatrixBase<Derived2> &v2)
{
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "angle(): first parameter is not a 2D vector");
static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "angle(): second parameter is not a 2D vector");
auto v1d = v1.template cast<double>();
auto v2d = v2.template cast<double>();
return atan2(cross2(v1d, v2d), v1d.dot(v2d));
}
// ***************************
struct GlobalModelInfo;
struct SeamComparator;
enum class EnforcedBlockedSeamPoint {
Blocked = 0,
Neutral = 1,
Enforced = 2,
};
// struct representing single perimeter loop
struct Perimeter
{
size_t start_index{};
size_t end_index{}; // inclusive!
size_t seam_index{};
float flow_width{};
// During alignment, a final position may be stored here. In that case, finalized is set to true.
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
// Random position also uses this flexibility to set final seam point position
bool finalized = false;
Vec3f final_seam_position = Vec3f::Zero();
};
// Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created,
// then all the needed attributes are computed and finally, for each perimeter one point is chosen as seam.
// This seam position can be then further aligned
struct SeamCandidate
{
SeamCandidate(const Vec3f &pos, Perimeter &perimeter, float local_ccw_angle, EnforcedBlockedSeamPoint type)
: position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle(local_ccw_angle), type(type), central_enforcer(false)
{}
const Vec3f position;
// pointer to Perimeter loop of this point. It is shared across all points of the loop
Perimeter &perimeter;
float visibility;
float overhang;
// distance inside the merged layer regions, for detecting perimeter points which are hidden indside the print (e.g. multimaterial join)
// Negative sign means inside the print, comes from EdgeGrid structure
float embedded_distance;
float local_ccw_angle;
EnforcedBlockedSeamPoint type;
bool central_enforcer; // marks this candidate as central point of enforced segment on the perimeter - important for alignment
};
struct SeamCandidateCoordinateFunctor
{
SeamCandidateCoordinateFunctor(const std::vector<SeamCandidate> &seam_candidates) : seam_candidates(seam_candidates) {}
const std::vector<SeamCandidate> &seam_candidates;
float operator()(size_t index, size_t dim) const { return seam_candidates[index].position[dim]; }
};
} // namespace SeamPlacerImpl
struct PrintObjectSeamData
{
using SeamCandidatesTree = KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
struct LayerSeams
{
Slic3r::deque<SeamPlacerImpl::Perimeter> perimeters;
std::vector<SeamPlacerImpl::SeamCandidate> points;
std::unique_ptr<SeamCandidatesTree> points_tree;
};
// Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter
std::vector<LayerSeams> layers;
// Map of PrintObjects (PO) -> vector of layers of PO -> unique_ptr to KD
// tree of all points of the given layer
void clear() { layers.clear(); }
};
class SeamPlacer
{
public:
// Number of samples generated on the mesh. There are sqr_rays_per_sample_point*sqr_rays_per_sample_point rays casted from each samples
static constexpr size_t raycasting_visibility_samples_count = 30000;
// square of number of rays per sample point
static constexpr size_t sqr_rays_per_sample_point = 5;
// snapping angle - angles larger than this value will be snapped to during seam painting
static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f;
// overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction
//BBS
static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f;
// determines angle importance compared to visibility ( neutral value is 1.0f. )
static constexpr float angle_importance_aligned = 0.6f;
static constexpr float angle_importance_nearest = 1.0f; // use much higher angle importance for nearest mode, to combat the visibility info noise
// For long polygon sides, if they are close to the custom seam drawings, they are oversampled with this step size
static constexpr float enforcer_oversampling_distance = 0.2f;
// When searching for seam clusters for alignment:
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
static constexpr float seam_align_score_tolerance = 0.3f;
// seam_align_tolerable_dist_factor - how far to search for seam from current position, final dist is seam_align_tolerable_dist_factor * flow_width
static constexpr float seam_align_tolerable_dist_factor = 4.0f;
// minimum number of seams needed in cluster to make alignment happen
static constexpr size_t seam_align_minimum_string_seams = 6;
// millimeters covered by spline; determines number of splines for the given string
static constexpr size_t seam_align_mm_per_segment = 4.0f;
// The following data structures hold all perimeter points for all PrintObject.
std::unordered_map<const PrintObject *, PrintObjectSeamData> m_seam_per_object;
void init(const Print &print, std::function<void(void)> throw_if_canceled_func);
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const;
private:
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference);
void calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_overhangs_and_layer_embedding(const PrintObject *po);
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
std::vector<std::pair<size_t, size_t>> find_seam_string(const PrintObject *po, std::pair<size_t, size_t> start_seam, const SeamPlacerImpl::SeamComparator &comparator) const;
std::optional<std::pair<size_t, size_t>> find_next_seam_in_layer(const std::vector<PrintObjectSeamData::LayerSeams> &layers,
const Vec3f & projected_position,
const size_t layer_idx,
const float max_distance,
const SeamPlacerImpl::SeamComparator & comparator) const;
};
} // namespace Slic3r
#endif // libslic3r_SeamPlacer_hpp_

View file

@ -474,7 +474,8 @@ void ToolOrdering::reorder_extruders(std::vector<unsigned int> tool_order_layer0
if (m_layer_tools.empty())
return;
assert(!tool_order_layer0.empty());
if (tool_order_layer0.empty())
return;
// Reorder the extruders of first layer
{

View file

@ -0,0 +1,291 @@
#ifndef BICUBIC_HPP
#define BICUBIC_HPP
#include <algorithm>
#include <vector>
#include <cmath>
#include <Eigen/Dense>
namespace Slic3r {
namespace Geometry {
namespace BicubicInternal {
// Linear kernel, to be able to test cubic methods with hat kernels.
template<typename T>
struct LinearKernel
{
typedef T FloatType;
static T a00() {
return T(0.);
}
static T a01() {
return T(0.);
}
static T a02() {
return T(0.);
}
static T a03() {
return T(0.);
}
static T a10() {
return T(1.);
}
static T a11() {
return T(-1.);
}
static T a12() {
return T(0.);
}
static T a13() {
return T(0.);
}
static T a20() {
return T(0.);
}
static T a21() {
return T(1.);
}
static T a22() {
return T(0.);
}
static T a23() {
return T(0.);
}
static T a30() {
return T(0.);
}
static T a31() {
return T(0.);
}
static T a32() {
return T(0.);
}
static T a33() {
return T(0.);
}
};
// Interpolation kernel aka Catmul-Rom aka Keyes kernel.
template<typename T>
struct CubicCatmulRomKernel
{
typedef T FloatType;
static T a00() {
return 0;
}
static T a01() {
return T( -0.5);
}
static T a02() {
return T( 1.);
}
static T a03() {
return T( -0.5);
}
static T a10() {
return T( 1.);
}
static T a11() {
return 0;
}
static T a12() {
return T( -5. / 2.);
}
static T a13() {
return T( 3. / 2.);
}
static T a20() {
return 0;
}
static T a21() {
return T( 0.5);
}
static T a22() {
return T( 2.);
}
static T a23() {
return T( -3. / 2.);
}
static T a30() {
return 0;
}
static T a31() {
return 0;
}
static T a32() {
return T( -0.5);
}
static T a33() {
return T( 0.5);
}
};
// B-spline kernel
template<typename T>
struct CubicBSplineKernel
{
typedef T FloatType;
static T a00() {
return T( 1. / 6.);
}
static T a01() {
return T( -3. / 6.);
}
static T a02() {
return T( 3. / 6.);
}
static T a03() {
return T( -1. / 6.);
}
static T a10() {
return T( 4. / 6.);
}
static T a11() {
return 0;
}
static T a12() {
return T( -6. / 6.);
}
static T a13() {
return T( 3. / 6.);
}
static T a20() {
return T( 1. / 6.);
}
static T a21() {
return T( 3. / 6.);
}
static T a22() {
return T( 3. / 6.);
}
static T a23() {
return T( -3. / 6.);
}
static T a30() {
return 0;
}
static T a31() {
return 0;
}
static T a32() {
return 0;
}
static T a33() {
return T( 1. / 6.);
}
};
template<class T>
inline T clamp(T a, T lower, T upper)
{
return (a < lower) ? lower :
(a > upper) ? upper : a;
}
}
template<typename Kernel>
struct CubicKernelWrapper
{
typedef typename Kernel::FloatType FloatType;
static constexpr size_t kernel_span = 4;
static FloatType kernel(FloatType x)
{
x = fabs(x);
if (x >= (FloatType) 2.)
return 0.0f;
if (x <= (FloatType) 1.) {
FloatType x2 = x * x;
FloatType x3 = x2 * x;
return Kernel::a10() + Kernel::a11() * x + Kernel::a12() * x2 + Kernel::a13() * x3;
}
assert(x > (FloatType )1. && x < (FloatType )2.);
x -= (FloatType) 1.;
FloatType x2 = x * x;
FloatType x3 = x2 * x;
return Kernel::a00() + Kernel::a01() * x + Kernel::a02() * x2 + Kernel::a03() * x3;
}
static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x)
{
const FloatType x2 = x * x;
const FloatType x3 = x * x * x;
return f0 * (Kernel::a00() + Kernel::a01() * x + Kernel::a02() * x2 + Kernel::a03() * x3) +
f1 * (Kernel::a10() + Kernel::a11() * x + Kernel::a12() * x2 + Kernel::a13() * x3) +
f2 * (Kernel::a20() + Kernel::a21() * x + Kernel::a22() * x2 + Kernel::a23() * x3) +
f3 * (Kernel::a30() + Kernel::a31() * x + Kernel::a32() * x2 + Kernel::a33() * x3);
}
};
// Linear splines
template<typename NumberType>
using LinearKernel = CubicKernelWrapper<BicubicInternal::LinearKernel<NumberType>>;
// Catmul-Rom splines
template<typename NumberType>
using CubicCatmulRomKernel = CubicKernelWrapper<BicubicInternal::CubicCatmulRomKernel<NumberType>>;
// Cubic B-splines
template<typename NumberType>
using CubicBSplineKernel = CubicKernelWrapper<BicubicInternal::CubicBSplineKernel<NumberType>>;
template<typename KernelWrapper>
static typename KernelWrapper::FloatType cubic_interpolate(const Eigen::ArrayBase<typename KernelWrapper::FloatType> &F,
const typename KernelWrapper::FloatType pt) {
typedef typename KernelWrapper::FloatType T;
const int w = int(F.size());
const int ix = (int) floor(pt);
const T s = pt - T( ix);
if (ix > 1 && ix + 2 < w) {
// Inside the fully interpolated region.
return KernelWrapper::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s);
}
// Transition region. Extend with a constant function.
auto f = [&F, w](T x) {
return F[BicubicInternal::clamp(x, 0, w - 1)];
};
return KernelWrapper::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s);
}
template<typename Kernel, typename Derived>
static float bicubic_interpolate(const Eigen::MatrixBase<Derived> &F,
const Eigen::Matrix<typename Kernel::FloatType, 2, 1, Eigen::DontAlign> &pt) {
typedef typename Kernel::FloatType T;
const int w = F.cols();
const int h = F.rows();
const int ix = (int) floor(pt[0]);
const int iy = (int) floor(pt[1]);
const T s = pt[0] - T( ix);
const T t = pt[1] - T( iy);
if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) {
// Inside the fully interpolated region.
return Kernel::interpolate(
Kernel::interpolate(F(ix - 1, iy - 1), F(ix, iy - 1), F(ix + 1, iy - 1), F(ix + 2, iy - 1), s),
Kernel::interpolate(F(ix - 1, iy), F(ix, iy), F(ix + 1, iy), F(ix + 2, iy), s),
Kernel::interpolate(F(ix - 1, iy + 1), F(ix, iy + 1), F(ix + 1, iy + 1), F(ix + 2, iy + 1), s),
Kernel::interpolate(F(ix - 1, iy + 2), F(ix, iy + 2), F(ix + 1, iy + 2), F(ix + 2, iy + 2), s), t);
}
// Transition region. Extend with a constant function.
auto f = [&F, w, h](int x, int y) {
return F(BicubicInternal::clamp(x, 0, w - 1), BicubicInternal::clamp(y, 0, h - 1));
};
return Kernel::interpolate(
Kernel::interpolate(f(ix - 1, iy - 1), f(ix, iy - 1), f(ix + 1, iy - 1), f(ix + 2, iy - 1), s),
Kernel::interpolate(f(ix - 1, iy), f(ix, iy), f(ix + 1, iy), f(ix + 2, iy), s),
Kernel::interpolate(f(ix - 1, iy + 1), f(ix, iy + 1), f(ix + 1, iy + 1), f(ix + 2, iy + 1), s),
Kernel::interpolate(f(ix - 1, iy + 2), f(ix, iy + 2), f(ix + 1, iy + 2), f(ix + 2, iy + 2), s), t);
}
} //namespace Geometry
} // namespace Slic3r
#endif /* BICUBIC_HPP */

View file

@ -0,0 +1,218 @@
#ifndef SRC_LIBSLIC3R_GEOMETRY_CURVES_HPP_
#define SRC_LIBSLIC3R_GEOMETRY_CURVES_HPP_
#include "libslic3r/Point.hpp"
#include "Bicubic.hpp"
#include <iostream>
//#define LSQR_DEBUG
namespace Slic3r {
namespace Geometry {
template<int Dimension, typename NumberType>
struct PolynomialCurve {
Eigen::MatrixXf coefficients;
Vec<Dimension, NumberType> get_fitted_value(const NumberType& value) const {
Vec<Dimension, NumberType> result = Vec<Dimension, NumberType>::Zero();
size_t order = this->coefficients.rows() - 1;
auto x = NumberType(1.);
for (size_t index = 0; index < order + 1; ++index, x *= value)
result += x * this->coefficients.col(index);
return result;
}
};
//https://towardsdatascience.com/least-square-polynomial-CURVES-using-c-eigen-package-c0673728bd01
template<int Dimension, typename NumberType>
PolynomialCurve<Dimension, NumberType> fit_polynomial(const std::vector<Vec<Dimension, NumberType>> &observations,
const std::vector<NumberType> &observation_points,
const std::vector<NumberType> &weights, size_t order) {
// check to make sure inputs are correct
size_t cols = order + 1;
assert(observation_points.size() >= cols);
assert(observation_points.size() == weights.size());
assert(observations.size() == weights.size());
Eigen::MatrixXf data_points(Dimension, observations.size());
Eigen::MatrixXf T(observations.size(), cols);
for (size_t i = 0; i < weights.size(); ++i) {
auto squared_weight = sqrt(weights[i]);
data_points.col(i) = observations[i] * squared_weight;
// Populate the matrix
auto x = squared_weight;
auto c = observation_points[i];
for (size_t j = 0; j < cols; ++j, x *= c)
T(i, j) = x;
}
const auto QR = T.householderQr();
Eigen::MatrixXf coefficients(Dimension, cols);
// Solve for linear least square fit
for (size_t dim = 0; dim < Dimension; ++dim) {
coefficients.row(dim) = QR.solve(data_points.row(dim).transpose());
}
return {std::move(coefficients)};
}
template<size_t Dimension, typename NumberType, typename KernelType>
struct PiecewiseFittedCurve {
using Kernel = KernelType;
Eigen::MatrixXf coefficients;
NumberType start;
NumberType segment_size;
size_t endpoints_level_of_freedom;
Vec<Dimension, NumberType> get_fitted_value(const NumberType &observation_point) const {
Vec<Dimension, NumberType> result = Vec<Dimension, NumberType>::Zero();
//find corresponding segment index; expects kernels to be centered
int middle_right_segment_index = floor((observation_point - start) / segment_size);
//find index of first segment that is affected by the point i; this can be deduced from kernel_span
int start_segment_idx = middle_right_segment_index - Kernel::kernel_span / 2 + 1;
for (int segment_index = start_segment_idx; segment_index < int(start_segment_idx + Kernel::kernel_span);
segment_index++) {
NumberType segment_start = start + segment_index * segment_size;
NumberType normalized_segment_distance = (segment_start - observation_point) / segment_size;
int parameter_index = segment_index + endpoints_level_of_freedom;
parameter_index = std::clamp(parameter_index, 0, int(coefficients.cols()) - 1);
result += Kernel::kernel(normalized_segment_distance) * coefficients.col(parameter_index);
}
return result;
}
};
// observations: data to be fitted by the curve
// observation points: growing sequence of points where the observations were made.
// In other words, for function f(x) = y, observations are y0...yn, and observation points are x0...xn
// weights: how important the observation is
// segments_count: number of segments inside the valid length of the curve
// endpoints_level_of_freedom: number of additional parameters at each end; reasonable values depend on the kernel span
template<typename Kernel, int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
const std::vector<Vec<Dimension, NumberType>> &observations,
const std::vector<NumberType> &observation_points,
const std::vector<NumberType> &weights,
size_t segments_count,
size_t endpoints_level_of_freedom) {
// check to make sure inputs are correct
assert(segments_count > 0);
assert(observations.size() > 0);
assert(observation_points.size() == observations.size());
assert(observation_points.size() == weights.size());
assert(segments_count <= observations.size());
//prepare sqrt of weights, which will then be applied to both matrix T and observed data: https://en.wikipedia.org/wiki/Weighted_least_squares
std::vector<NumberType> sqrt_weights(weights.size());
for (size_t index = 0; index < weights.size(); ++index) {
assert(weights[index] > 0);
sqrt_weights[index] = sqrt(weights[index]);
}
// prepare result and compute metadata
PiecewiseFittedCurve<Dimension, NumberType, Kernel> result { };
NumberType valid_length = observation_points.back() - observation_points.front();
NumberType segment_size = valid_length / NumberType(segments_count);
result.start = observation_points.front();
result.segment_size = segment_size;
result.endpoints_level_of_freedom = endpoints_level_of_freedom;
// prepare observed data
// Eigen defaults to column major memory layout.
Eigen::MatrixXf data_points(Dimension, observations.size());
for (size_t index = 0; index < observations.size(); ++index) {
data_points.col(index) = observations[index] * sqrt_weights[index];
}
// parameters count is always increased by one to make the parametric space of the curve symmetric.
// without this fix, the end of the curve is less flexible than the beginning
size_t parameters_count = segments_count + 1 + 2 * endpoints_level_of_freedom;
//Create weight matrix T for each point and each segment;
Eigen::MatrixXf T(observation_points.size(), parameters_count);
T.setZero();
//Fill the weight matrix
for (size_t i = 0; i < observation_points.size(); ++i) {
NumberType observation_point = observation_points[i];
//find corresponding segment index; expects kernels to be centered
int middle_right_segment_index = floor((observation_point - result.start) / result.segment_size);
//find index of first segment that is affected by the point i; this can be deduced from kernel_span
int start_segment_idx = middle_right_segment_index - int(Kernel::kernel_span / 2) + 1;
for (int segment_index = start_segment_idx; segment_index < int(start_segment_idx + Kernel::kernel_span);
segment_index++) {
NumberType segment_start = result.start + segment_index * result.segment_size;
NumberType normalized_segment_distance = (segment_start - observation_point) / result.segment_size;
int parameter_index = segment_index + endpoints_level_of_freedom;
parameter_index = std::clamp(parameter_index, 0, int(parameters_count) - 1);
T(i, parameter_index) += Kernel::kernel(normalized_segment_distance) * sqrt_weights[i];
}
}
#ifdef LSQR_DEBUG
std::cout << "weight matrix: " << std::endl;
for (int obs = 0; obs < observation_points.size(); ++obs) {
std::cout << std::endl;
for (int segment = 0; segment < parameters_count; ++segment) {
std::cout << T(obs, segment) << " ";
}
}
std::cout << std::endl;
#endif
// Solve for linear least square fit
result.coefficients.resize(Dimension, parameters_count);
const auto QR = T.fullPivHouseholderQr();
for (size_t dim = 0; dim < Dimension; ++dim) {
result.coefficients.row(dim) = QR.solve(data_points.row(dim).transpose());
}
return result;
}
template<int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, LinearKernel<NumberType>>
fit_linear_spline(
const std::vector<Vec<Dimension, NumberType>> &observations,
std::vector<NumberType> observation_points,
std::vector<NumberType> weights,
size_t segments_count,
size_t endpoints_level_of_freedom = 0) {
return fit_curve<LinearKernel<NumberType>>(observations, observation_points, weights, segments_count,
endpoints_level_of_freedom);
}
template<int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, CubicBSplineKernel<NumberType>>
fit_cubic_bspline(
const std::vector<Vec<Dimension, NumberType>> &observations,
std::vector<NumberType> observation_points,
std::vector<NumberType> weights,
size_t segments_count,
size_t endpoints_level_of_freedom = 0) {
return fit_curve<CubicBSplineKernel<NumberType>>(observations, observation_points, weights, segments_count,
endpoints_level_of_freedom);
}
template<int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, CubicCatmulRomKernel<NumberType>>
fit_catmul_rom_spline(
const std::vector<Vec<Dimension, NumberType>> &observations,
std::vector<NumberType> observation_points,
std::vector<NumberType> weights,
size_t segments_count,
size_t endpoints_level_of_freedom = 0) {
return fit_curve<CubicCatmulRomKernel<NumberType>>(observations, observation_points, weights, segments_count,
endpoints_level_of_freedom);
}
}
}
#endif /* SRC_LIBSLIC3R_GEOMETRY_CURVES_HPP_ */

View file

@ -11,224 +11,362 @@
namespace Slic3r {
enum class VisitorReturnMask : unsigned int {
CONTINUE_LEFT = 1,
CONTINUE_RIGHT = 2,
STOP = 4,
};
// KD tree for N-dimensional closest point search.
template<size_t ANumDimensions, typename ACoordType, typename ACoordinateFn>
class KDTreeIndirect
{
public:
static constexpr size_t NumDimensions = ANumDimensions;
using CoordinateFn = ACoordinateFn;
using CoordType = ACoordType;
static constexpr size_t NumDimensions = ANumDimensions;
using CoordinateFn = ACoordinateFn;
using CoordType = ACoordType;
// Following could be static constexpr size_t, but that would not link in C++11
enum : size_t {
npos = size_t(-1)
};
KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(std::move(indices)); }
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> &&indices) : coordinate(coordinate) { this->build(std::move(indices)); }
KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
void clear() { m_nodes.clear(); }
KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(indices); }
KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
void clear() { m_nodes.clear(); }
void build(size_t num_indices)
{
std::vector<size_t> indices;
indices.reserve(num_indices);
for (size_t i = 0; i < num_indices; ++ i)
indices.emplace_back(i);
this->build(std::move(indices));
}
void build(size_t num_indices)
{
std::vector<size_t> indices;
indices.reserve(num_indices);
for (size_t i = 0; i < num_indices; ++ i)
indices.emplace_back(i);
this->build(indices);
}
void build(std::vector<size_t> &&indices)
{
if (indices.empty())
clear();
else {
// Allocate enough memory for a full binary tree.
m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos);
build_recursive(indices, 0, 0, 0, indices.size() - 1);
}
indices.clear();
}
void build(std::vector<size_t> &indices)
{
if (indices.empty())
clear();
else {
// Allocate enough memory for a full binary tree.
m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos);
build_recursive(indices, 0, 0, 0, indices.size() - 1);
}
indices.clear();
}
enum class VisitorReturnMask : unsigned int
{
CONTINUE_LEFT = 1,
CONTINUE_RIGHT = 2,
STOP = 4,
};
template<typename CoordType>
unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
{
CoordType dist = point_coord - this->coordinate(idx, dimension);
return (dist * dist < search_radius + CoordType(EPSILON)) ?
// The plane intersects a hypersphere centered at point_coord of search_radius.
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
// The plane does not intersect the hypersphere.
(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
}
template<typename CoordType>
unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
{
CoordType dist = point_coord - this->coordinate(idx, dimension);
return (dist * dist < search_radius + CoordType(EPSILON)) ?
// The plane intersects a hypersphere centered at point_coord of search_radius.
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
// The plane does not intersect the hypersphere.
(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
}
// Visitor is supposed to return a bit mask of VisitorReturnMask.
template<typename Visitor>
void visit(Visitor &visitor) const
{
// Visitor is supposed to return a bit mask of VisitorReturnMask.
template<typename Visitor>
void visit(Visitor &visitor) const
{
visit_recursive(0, 0, visitor);
}
}
CoordinateFn coordinate;
CoordinateFn coordinate;
private:
// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
void build_recursive(std::vector<size_t> &input, size_t node, const size_t dimension, const size_t left, const size_t right)
{
if (left > right)
return;
// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
void build_recursive(std::vector<size_t> &input, size_t node, const size_t dimension, const size_t left, const size_t right)
{
if (left > right)
return;
assert(node < m_nodes.size());
assert(node < m_nodes.size());
if (left == right) {
// Insert a node into the balanced tree.
m_nodes[node] = input[left];
return;
}
if (left == right) {
// Insert a node into the balanced tree.
m_nodes[node] = input[left];
return;
}
// Partition the input to left / right pieces of the same length to produce a balanced tree.
size_t center = (left + right) / 2;
partition_input(input, dimension, left, right, center);
// Insert a node into the tree.
m_nodes[node] = input[center];
// Build up the left / right subtrees.
size_t next_dimension = dimension;
if (++ next_dimension == NumDimensions)
next_dimension = 0;
if (center > left)
build_recursive(input, node * 2 + 1, next_dimension, left, center - 1);
build_recursive(input, node * 2 + 2, next_dimension, center + 1, right);
}
// Partition the input to left / right pieces of the same length to produce a balanced tree.
size_t center = (left + right) / 2;
partition_input(input, dimension, left, right, center);
// Insert a node into the tree.
m_nodes[node] = input[center];
// Build up the left / right subtrees.
size_t next_dimension = dimension;
if (++ next_dimension == NumDimensions)
next_dimension = 0;
if (center > left)
build_recursive(input, node * 2 + 1, next_dimension, left, center - 1);
build_recursive(input, node * 2 + 2, next_dimension, center + 1, right);
}
// Partition the input m_nodes <left, right> at "k" and "dimension" using the QuickSelect method:
// https://en.wikipedia.org/wiki/Quickselect
// Items left of the k'th item are lower than the k'th item in the "dimension",
// items right of the k'th item are higher than the k'th item in the "dimension",
void partition_input(std::vector<size_t> &input, const size_t dimension, size_t left, size_t right, const size_t k) const
{
while (left < right) {
size_t center = (left + right) / 2;
CoordType pivot;
{
// Bubble sort the input[left], input[center], input[right], so that a median of the three values
// will end up in input[center].
CoordType left_value = this->coordinate(input[left], dimension);
CoordType center_value = this->coordinate(input[center], dimension);
CoordType right_value = this->coordinate(input[right], dimension);
if (left_value > center_value) {
std::swap(input[left], input[center]);
std::swap(left_value, center_value);
}
if (left_value > right_value) {
std::swap(input[left], input[right]);
right_value = left_value;
}
if (center_value > right_value) {
std::swap(input[center], input[right]);
center_value = right_value;
}
pivot = center_value;
}
if (right <= left + 2)
// The <left, right> interval is already sorted.
break;
size_t i = left;
size_t j = right - 1;
std::swap(input[center], input[j]);
// Partition the set based on the pivot.
for (;;) {
// Skip left points that are already at correct positions.
// Search will certainly stop at position (right - 1), which stores the pivot.
while (this->coordinate(input[++ i], dimension) < pivot) ;
// Skip right points that are already at correct positions.
while (this->coordinate(input[-- j], dimension) > pivot && i < j) ;
if (i >= j)
break;
std::swap(input[i], input[j]);
}
// Restore pivot to the center of the sequence.
std::swap(input[i], input[right - 1]);
// Which side the kth element is in?
if (k < i)
right = i - 1;
else if (k == i)
// Sequence is partitioned, kth element is at its place.
break;
else
left = i + 1;
}
}
// Partition the input m_nodes <left, right> at "k" and "dimension" using the QuickSelect method:
// https://en.wikipedia.org/wiki/Quickselect
// Items left of the k'th item are lower than the k'th item in the "dimension",
// items right of the k'th item are higher than the k'th item in the "dimension",
void partition_input(std::vector<size_t> &input, const size_t dimension, size_t left, size_t right, const size_t k) const
{
while (left < right) {
size_t center = (left + right) / 2;
CoordType pivot;
{
// Bubble sort the input[left], input[center], input[right], so that a median of the three values
// will end up in input[center].
CoordType left_value = this->coordinate(input[left], dimension);
CoordType center_value = this->coordinate(input[center], dimension);
CoordType right_value = this->coordinate(input[right], dimension);
if (left_value > center_value) {
std::swap(input[left], input[center]);
std::swap(left_value, center_value);
}
if (left_value > right_value) {
std::swap(input[left], input[right]);
right_value = left_value;
}
if (center_value > right_value) {
std::swap(input[center], input[right]);
center_value = right_value;
}
pivot = center_value;
}
if (right <= left + 2)
// The <left, right> interval is already sorted.
break;
size_t i = left;
size_t j = right - 1;
std::swap(input[center], input[j]);
// Partition the set based on the pivot.
for (;;) {
// Skip left points that are already at correct positions.
// Search will certainly stop at position (right - 1), which stores the pivot.
while (this->coordinate(input[++ i], dimension) < pivot) ;
// Skip right points that are already at correct positions.
while (this->coordinate(input[-- j], dimension) > pivot && i < j) ;
if (i >= j)
break;
std::swap(input[i], input[j]);
}
// Restore pivot to the center of the sequence.
std::swap(input[i], input[right - 1]);
// Which side the kth element is in?
if (k < i)
right = i - 1;
else if (k == i)
// Sequence is partitioned, kth element is at its place.
break;
else
left = i + 1;
}
}
template<typename Visitor>
void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const
{
assert(! m_nodes.empty());
if (node >= m_nodes.size() || m_nodes[node] == npos)
return;
template<typename Visitor>
void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const
{
assert(! m_nodes.empty());
if (node >= m_nodes.size() || m_nodes[node] == npos)
return;
// Left / right child node index.
size_t left = node * 2 + 1;
size_t right = left + 1;
unsigned int mask = visitor(m_nodes[node], dimension);
if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) {
size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT)
visit_recursive(left, next_dimension, visitor);
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT)
visit_recursive(right, next_dimension, visitor);
}
}
// Left / right child node index.
size_t left = node * 2 + 1;
size_t right = left + 1;
unsigned int mask = visitor(m_nodes[node], dimension);
if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) {
size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT)
visit_recursive(left, next_dimension, visitor);
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT)
visit_recursive(right, next_dimension, visitor);
}
}
std::vector<size_t> m_nodes;
std::vector<size_t> m_nodes;
};
// Find a closest point using Euclidian metrics.
// Returns npos if not found.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter)
template<size_t K,
typename PointType,
typename FilterFn,
size_t D,
typename CoordT,
typename CoordFn>
std::array<size_t, K> find_closest_points(
const KDTreeIndirect<D, CoordT, CoordFn> &kdtree,
const PointType &point,
FilterFn filter)
{
using CoordType = typename KDTreeIndirectType::CoordType;
using Tree = KDTreeIndirect<D, CoordT, CoordFn>;
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType &point;
const FilterFn filter;
size_t min_idx = KDTreeIndirectType::npos;
CoordType min_dist = std::numeric_limits<CoordType>::max();
struct Visitor
{
const Tree &kdtree;
const PointType &point;
const FilterFn filter;
Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {}
unsigned int operator()(size_t idx, size_t dimension) {
if (this->filter(idx)) {
auto dist = CoordType(0);
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) {
CoordType d = point[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
if (dist < min_dist) {
min_dist = dist;
min_idx = idx;
}
}
return kdtree.descent_mask(point[dimension], min_dist, idx, dimension);
}
} visitor(kdtree, point, filter);
std::array<std::pair<size_t, CoordT>, K> results;
kdtree.visit(visitor);
return visitor.min_idx;
Visitor(const Tree &kdtree, const PointType &point, FilterFn filter)
: kdtree(kdtree), point(point), filter(filter)
{
results.fill(std::make_pair(Tree::npos,
std::numeric_limits<CoordT>::max()));
}
unsigned int operator()(size_t idx, size_t dimension)
{
if (this->filter(idx)) {
auto dist = CoordT(0);
for (size_t i = 0; i < D; ++i) {
CoordT d = point[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
auto res = std::make_pair(idx, dist);
auto it = std::lower_bound(results.begin(), results.end(),
res, [](auto &r1, auto &r2) {
return r1.second < r2.second;
});
if (it != results.end()) {
std::rotate(it, std::prev(results.end()), results.end());
*it = res;
}
}
return kdtree.descent_mask(point[dimension],
results.front().second, idx,
dimension);
}
} visitor(kdtree, point, filter);
kdtree.visit(visitor);
std::array<size_t, K> ret;
for (size_t i = 0; i < K; i++) ret[i] = visitor.results[i].first;
return ret;
}
template<size_t K, typename PointType, size_t D, typename CoordT, typename CoordFn>
std::array<size_t, K> find_closest_points(
const KDTreeIndirect<D, CoordT, CoordFn> &kdtree, const PointType &point)
{
return find_closest_points<K>(kdtree, point, [](size_t) { return true; });
}
template<typename PointType,
typename FilterFn,
size_t D,
typename CoordT,
typename CoordFn>
size_t find_closest_point(const KDTreeIndirect<D, CoordT, CoordFn> &kdtree,
const PointType &point,
FilterFn filter)
{
return find_closest_points<1>(kdtree, point, filter)[0];
}
template<typename KDTreeIndirectType, typename PointType>
size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point)
{
return find_closest_point(kdtree, point, [](size_t) { return true; });
return find_closest_point(kdtree, point, [](size_t) { return true; });
}
// Find nearby points (spherical neighbourhood) using Euclidian metrics.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center,
const typename KDTreeIndirectType::CoordType& max_distance, FilterFn filter)
{
using CoordType = typename KDTreeIndirectType::CoordType;
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType center;
const CoordType max_distance_squared;
const FilterFn filter;
std::vector<size_t> result;
Visitor(const KDTreeIndirectType &kdtree, const PointType& center, const CoordType &max_distance,
FilterFn filter) :
kdtree(kdtree), center(center), max_distance_squared(max_distance*max_distance), filter(filter) {
}
unsigned int operator()(size_t idx, size_t dimension) {
if (this->filter(idx)) {
auto dist = CoordType(0);
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) {
CoordType d = center[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
if (dist < max_distance_squared) {
result.push_back(idx);
}
}
return kdtree.descent_mask(center[dimension], max_distance_squared, idx, dimension);
}
} visitor(kdtree, center, max_distance, filter);
kdtree.visit(visitor);
return visitor.result;
}
template<typename KDTreeIndirectType, typename PointType>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center,
const typename KDTreeIndirectType::CoordType& max_distance)
{
return find_nearby_points(kdtree, center, max_distance, [](size_t) {
return true;
});
}
// Find nearby points (spherical neighbourhood) using Euclidian metrics.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree,
const PointType &bb_min,
const PointType &bb_max,
FilterFn filter)
{
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType &bb_min, &bb_max;
const FilterFn filter;
std::vector<size_t> result;
Visitor(const KDTreeIndirectType &kdtree, const PointType& bbmin, const PointType& bbmax,
FilterFn filter) :
kdtree(kdtree), bb_min{bbmin}, bb_max{bbmax}, filter(filter) {
}
unsigned int operator()(size_t idx, size_t dimension) {
unsigned int ret =
static_cast<unsigned int>(VisitorReturnMask::CONTINUE_LEFT) |
static_cast<unsigned int>(VisitorReturnMask::CONTINUE_RIGHT);
if (this->filter(idx)) {
PointType p;
bool contains = true;
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) {
p(i) = kdtree.coordinate(idx, i);
contains = contains && bb_min(i) <= p(i) && p(i) <= bb_max(i);
}
if (p(dimension) < bb_min(dimension))
ret = static_cast<unsigned int>(VisitorReturnMask::CONTINUE_RIGHT);
if (p(dimension) > bb_max(dimension))
ret = static_cast<unsigned int>(VisitorReturnMask::CONTINUE_LEFT);
if (contains)
result.emplace_back(idx);
}
return ret;
}
} visitor(kdtree, bb_min, bb_max, filter);
kdtree.visit(visitor);
return visitor.result;
}
} // namespace Slic3r

View file

@ -137,7 +137,7 @@ Model::~Model()
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions,
LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, bool *is_xxx, Semver* file_version, Import3mfProgressFn proFn,
ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project)
ImportstlProgressFn stlFn, ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project)
{
Model model;
@ -163,7 +163,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
boost::algorithm::iends_with(input_file, ".step"))
result = load_step(input_file.c_str(), &model, stepFn, stepIsUtf8Fn);
else if (boost::algorithm::iends_with(input_file, ".stl"))
result = load_stl(input_file.c_str(), &model);
result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
else if (boost::algorithm::iends_with(input_file, ".obj"))
result = load_obj(input_file.c_str(), &model);
//BBS: remove the old .amf.xml files
@ -185,10 +185,10 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (model.objects.empty())
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
for (ModelObject *o : model.objects)
o->input_file = input_file;
if (options & LoadStrategy::AddDefaultInstances)
model.add_default_instances();
@ -259,14 +259,14 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
//BBS
//CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_UPDATE_GCODE\n");
if (proFn) {
proFn(IMPORT_STAGE_UPDATE_GCODE, 0, 1, cb_cancel);
if (cb_cancel)
throw Slic3r::RuntimeError("Canceled");
}
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_CHECK_MODE_GCODE\n");
@ -416,7 +416,7 @@ void Model::collect_reusable_objects(std::vector<ObjectBase*>& objects)
std::mem_fn(&ObjectBase::id));
model_object->volumes.clear();
}
// we never own these objects
// we never own these objects
this->objects.clear();
}
@ -591,7 +591,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
assert(this->objects.size() >= 2);
if (this->objects.size() < 2)
return;
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string();
@ -601,7 +601,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
for (const ModelObject* o : this->objects)
for (const ModelVolume* v : o->volumes) {
// If there are more than one object, put all volumes together
// If there are more than one object, put all volumes together
// Each object may contain any number of volumes and instances
// The volumes transformations are relative to the object containing them...
Geometry::Transformation trafo_volume = v->get_transformation();
@ -620,7 +620,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
} else {
for (const ModelInstance* i : o->instances)
// ...so, transform everything to a common reference system (world)
copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume);
copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume);
}
}
@ -1010,6 +1010,20 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
return v;
}
ModelVolume* ModelObject::add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type /*= ModelVolumeType::INVALID*/)
{
ModelVolume* v = new ModelVolume(this, other.m_mesh);
if (type != ModelVolumeType::INVALID && v->type() != type)
v->set_type(type);
this->volumes.push_back(v);
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
// v->center_geometry_after_creation();
// this->invalidate_bounding_box();
// BBS: backup
Slic3r::save_object_mesh(*this);
return v;
}
void ModelObject::delete_volume(size_t idx)
{
ModelVolumePtrs::iterator i = this->volumes.begin() + idx;
@ -1324,7 +1338,7 @@ Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
void ModelObject::center_around_origin(bool include_modifiers)
{
// calculate the displacements needed to
// calculate the displacements needed to
// center this object around the origin
const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
@ -1476,8 +1490,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
// Perform conversion only if the target "imperial" state is different from the current one.
// This check supports conversion of "mixed" set of volumes, each with different "imperial" state.
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
vol->scale_geometry_after_creation(koef);
vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
@ -1538,7 +1552,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
bool keep_lower = attributes.has(ModelObjectCutAttribute::KeepLower);
bool cut_to_parts = attributes.has(ModelObjectCutAttribute::CutToParts);
ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr;
ModelObject* lower = cut_to_parts ? upper : (keep_lower ? ModelObject::new_clone(*this) : nullptr);
ModelObject* lower = (cut_to_parts&&upper!=nullptr) ? upper : (keep_lower ? ModelObject::new_clone(*this) : nullptr);
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
upper->set_model(nullptr);
@ -1598,7 +1612,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower->add_volume(*volume);
}
else if (! volume->mesh().empty()) {
else if (! volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
@ -1744,7 +1758,7 @@ ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
upper->add_volume(*volume);
upper->add_volume(*volume);
}
else if (!volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
@ -2185,7 +2199,7 @@ void ModelObject::print_info() const
using namespace std;
cout << fixed;
boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh();
BoundingBoxf3 bb = mesh.bounding_box();
Vec3d size = bb.size();
@ -2203,7 +2217,7 @@ void ModelObject::print_info() const
cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
if (! mesh.stats().manifold())
cout << "open_edges = " << mesh.stats().open_edges << endl;
if (mesh.stats().repaired()) {
const RepairedMeshErrors& stats = mesh.stats().repaired_errors;
if (stats.degenerate_facets > 0)
@ -2286,7 +2300,7 @@ void ModelVolume::set_material_id(t_model_material_id material_id)
}
ModelMaterial* ModelVolume::material() const
{
{
return this->object->get_model()->get_material(m_material_id);
}
@ -2362,8 +2376,10 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
Vec3d shift = this->mesh().bounding_box().center();
if (!shift.isApprox(Vec3d::Zero()))
{
if (m_mesh)
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
if (m_mesh) {
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
const_cast<TriangleMesh*>(m_mesh.get())->set_init_shift(shift);
}
if (m_convex_hull)
const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift);
@ -2803,7 +2819,7 @@ double Model::getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs)
}
return thermalLength;
}
// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered
// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered
double ModelInstance::get_auto_brim_width(double deltaT, double adhension) const
{
BoundingBoxf3 raw_bbox = object->raw_mesh_bounding_box();
@ -2896,7 +2912,7 @@ double ModelInstance::get_auto_brim_width() const
void ModelInstance::get_arrange_polygon(void* ap) const
{
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance =
@ -2909,7 +2925,7 @@ void ModelInstance::get_arrange_polygon(void* ap) const
// pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
// if (!pp.empty()) p = pp.front();
// }
arrangement::ArrangePolygon& ret = *(arrangement::ArrangePolygon*)ap;
ret.poly.contour = std::move(p);
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))};
@ -3039,6 +3055,12 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
}
}
bool FacetsAnnotation::equals(const FacetsAnnotation &other) const
{
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data = other.get_data();
return (m_data == data);
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new)
@ -3140,22 +3162,22 @@ bool model_property_changed(const ModelObject &model_object_old, const ModelObje
bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.supported_facets.timestamp_matches(mv_new.supported_facets); });
}
bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.seam_facets.timestamp_matches(mv_new.seam_facets); });
}
bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); });
}
@ -3189,7 +3211,7 @@ bool model_has_advanced_features(const Model &model)
void check_model_ids_validity(const Model &model)
{
std::set<ObjectID> ids;
auto check = [&ids](ObjectID id) {
auto check = [&ids](ObjectID id) {
assert(id.valid());
assert(ids.find(id) == ids.end());
ids.insert(id);

View file

@ -18,6 +18,8 @@
#include "Format/bbs_3mf.hpp"
//BBS: add step
#include "Format/STEP.hpp"
//BBS: add stl
#include "Format/STL.hpp"
#include <map>
#include <memory>
@ -306,6 +308,7 @@ public:
ModelVolume* add_volume(TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART);
ModelVolume* add_volume(const ModelVolume &volume, ModelVolumeType type = ModelVolumeType::INVALID);
ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh);
ModelVolume* add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type = ModelVolumeType::MODEL_PART);
void delete_volume(size_t idx);
void clear_volumes();
void sort_volumes(bool full_sort);
@ -615,6 +618,7 @@ public:
void set_triangle_from_string(int triangle_id, const std::string& str);
// After deserializing the last triangle, shrink data to fit.
void shrink_to_fit() { m_data.first.shrink_to_fit(); m_data.second.shrink_to_fit(); }
bool equals(const FacetsAnnotation &other) const;
private:
// Constructors to be only called by derived classes.
@ -674,6 +678,7 @@ public:
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
const TriangleMesh* mesh_ptr() const { return m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
@ -857,6 +862,18 @@ private:
if (mesh.facets_count() > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, const std::shared_ptr<const TriangleMesh> &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(mesh), m_type(type), object(object)
{
assert(this->id().valid());
assert(this->config.id().valid());
assert(this->supported_facets.id().valid());
assert(this->seam_facets.id().valid());
assert(this->mmu_segmentation_facets.id().valid());
assert(this->id() != this->config.id());
assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_facets.id());
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
assert(this->id().valid());
@ -1282,7 +1299,7 @@ public:
DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr,
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr,
std::vector<Preset*>* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr,
ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr);
ImportstlProgressFn stlFn = nullptr, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr);
// BBS
static double findMaxSpeed(const ModelObject* object);
static double getThermalLength(const ModelVolume* modelVolumePtr);

View file

@ -63,6 +63,23 @@ int MultiPoint::find_point(const Point &point) const
return -1; // not found
}
int MultiPoint::find_point(const Point &point, double scaled_epsilon) const
{
if (scaled_epsilon == 0) return this->find_point(point);
auto dist2_min = std::numeric_limits<double>::max();
auto eps2 = scaled_epsilon * scaled_epsilon;
int idx_min = -1;
for (const Point &pt : this->points) {
double d2 = (pt - point).cast<double>().squaredNorm();
if (d2 < dist2_min) {
idx_min = int(&pt - &this->points.front());
dist2_min = d2;
}
}
return dist2_min < eps2 ? idx_min : -1;
}
bool MultiPoint::has_boundary_point(const Point &point) const
{
double dist = (point.projection_onto(*this) - point).cast<double>().norm();

View file

@ -43,7 +43,12 @@ public:
double length() const;
bool is_valid() const { return this->points.size() >= 2; }
// Return index of a polygon point exactly equal to point.
// Return -1 if no such point exists.
int find_point(const Point &point) const;
// Return index of the closest point to point closer than scaled_epsilon.
// Return -1 if no such point exists.
int find_point(const Point &point, const double scaled_epsilon) const;
bool has_boundary_point(const Point &point) const;
int closest_point_index(const Point &point) const {
int idx = -1;

View file

@ -0,0 +1,142 @@
#include "NormalUtils.hpp"
using namespace Slic3r;
Vec3f NormalUtils::create_triangle_normal(
const stl_triangle_vertex_indices &indices,
const std::vector<stl_vertex> & vertices)
{
const stl_vertex &v0 = vertices[indices[0]];
const stl_vertex &v1 = vertices[indices[1]];
const stl_vertex &v2 = vertices[indices[2]];
Vec3f direction = (v1 - v0).cross(v2 - v0);
direction.normalize();
return direction;
}
std::vector<Vec3f> NormalUtils::create_triangle_normals(
const indexed_triangle_set &its)
{
std::vector<Vec3f> normals;
normals.reserve(its.indices.size());
for (const Vec3crd &index : its.indices) {
normals.push_back(create_triangle_normal(index, its.vertices));
}
return normals;
}
NormalUtils::Normals NormalUtils::create_normals_average_neighbor(
const indexed_triangle_set &its)
{
size_t count_vertices = its.vertices.size();
std::vector<Vec3f> normals(count_vertices, Vec3f(.0f, .0f, .0f));
std::vector<unsigned int> count(count_vertices, 0);
for (const Vec3crd &indice : its.indices) {
Vec3f normal = create_triangle_normal(indice, its.vertices);
for (int i = 0; i < 3; ++i) {
normals[indice[i]] += normal;
++count[indice[i]];
}
}
// normalize to size 1
for (auto &normal : normals) {
size_t index = &normal - &normals.front();
normal /= static_cast<float>(count[index]);
}
return normals;
}
// calc triangle angle of vertex defined by index to triangle indices
float NormalUtils::indice_angle(int i,
const Vec3crd & indice,
const std::vector<stl_vertex> &vertices)
{
int i1 = (i == 0) ? 2 : (i - 1);
int i2 = (i == 2) ? 0 : (i + 1);
Vec3f v1 = vertices[i1] - vertices[i];
Vec3f v2 = vertices[i2] - vertices[i];
v1.normalize();
v2.normalize();
float w = v1.dot(v2);
if (w > 1.f)
w = 1.f;
else if (w < -1.f)
w = -1.f;
return acos(w);
}
NormalUtils::Normals NormalUtils::create_normals_angle_weighted(
const indexed_triangle_set &its)
{
size_t count_vertices = its.vertices.size();
std::vector<Vec3f> normals(count_vertices, Vec3f(.0f, .0f, .0f));
std::vector<float> count(count_vertices, 0.f);
for (const Vec3crd &indice : its.indices) {
Vec3f normal = create_triangle_normal(indice, its.vertices);
Vec3f angles(indice_angle(0, indice, its.vertices),
indice_angle(1, indice, its.vertices), 0.f);
angles[2] = (M_PI - angles[0] - angles[1]);
for (int i = 0; i < 3; ++i) {
const float &weight = angles[i];
normals[indice[i]] += normal * weight;
count[indice[i]] += weight;
}
}
// normalize to size 1
for (auto &normal : normals) {
size_t index = &normal - &normals.front();
normal /= count[index];
}
return normals;
}
NormalUtils::Normals NormalUtils::create_normals_nelson_weighted(
const indexed_triangle_set &its)
{
size_t count_vertices = its.vertices.size();
std::vector<Vec3f> normals(count_vertices, Vec3f(.0f, .0f, .0f));
std::vector<float> count(count_vertices, 0.f);
const std::vector<stl_vertex> &vertices = its.vertices;
for (const Vec3crd &indice : its.indices) {
Vec3f normal = create_triangle_normal(indice, vertices);
const stl_vertex &v0 = vertices[indice[0]];
const stl_vertex &v1 = vertices[indice[1]];
const stl_vertex &v2 = vertices[indice[2]];
float e0 = (v0 - v1).norm();
float e1 = (v1 - v2).norm();
float e2 = (v2 - v0).norm();
Vec3f coefs(e0 * e2, e0 * e1, e1 * e2);
for (int i = 0; i < 3; ++i) {
const float &weight = coefs[i];
normals[indice[i]] += normal * weight;
count[indice[i]] += weight;
}
}
// normalize to size 1
for (auto &normal : normals) {
size_t index = &normal - &normals.front();
normal /= count[index];
}
return normals;
}
// calculate normals by averaging normals of neghbor triangles
std::vector<Vec3f> NormalUtils::create_normals(
const indexed_triangle_set &its, VertexNormalType type)
{
switch (type) {
case VertexNormalType::AverageNeighbor:
return create_normals_average_neighbor(its);
case VertexNormalType::AngleWeighted:
return create_normals_angle_weighted(its);
case VertexNormalType::NelsonMaxWeighted:
default:
return create_normals_nelson_weighted(its);
}
}

View file

@ -0,0 +1,69 @@
#ifndef slic3r_NormalUtils_hpp_
#define slic3r_NormalUtils_hpp_
#include "Point.hpp"
#include "Model.hpp"
namespace Slic3r {
/// <summary>
/// Collection of static function
/// to create normals
/// </summary>
class NormalUtils
{
public:
using Normal = Vec3f;
using Normals = std::vector<Normal>;
NormalUtils() = delete; // only static functions
enum class VertexNormalType {
AverageNeighbor,
AngleWeighted,
NelsonMaxWeighted
};
/// <summary>
/// Create normal for triangle defined by indices from vertices
/// </summary>
/// <param name="indices">index into vertices</param>
/// <param name="vertices">vector of vertices</param>
/// <returns>normal to triangle(normalized to size 1)</returns>
static Normal create_triangle_normal(
const stl_triangle_vertex_indices &indices,
const std::vector<stl_vertex> & vertices);
/// <summary>
/// Create normals for each vertices
/// </summary>
/// <param name="its">indices and vertices</param>
/// <returns>Vector of normals</returns>
static Normals create_triangle_normals(const indexed_triangle_set &its);
/// <summary>
/// Create normals for each vertex by averaging neighbor triangles normal
/// </summary>
/// <param name="its">Triangle indices and vertices</param>
/// <param name="type">Type of calculation normals</param>
/// <returns>Normal for each vertex</returns>
static Normals create_normals(
const indexed_triangle_set &its,
VertexNormalType type = VertexNormalType::NelsonMaxWeighted);
static Normals create_normals_average_neighbor(const indexed_triangle_set &its);
static Normals create_normals_angle_weighted(const indexed_triangle_set &its);
static Normals create_normals_nelson_weighted(const indexed_triangle_set &its);
/// <summary>
/// Calculate angle of trinagle side.
/// </summary>
/// <param name="i">index to indices, define angle point</param>
/// <param name="indice">address to vertices</param>
/// <param name="vertices">vertices data</param>
/// <returns>Angle [in radian]</returns>
static float indice_angle(int i,
const Vec3crd & indice,
const std::vector<stl_vertex> &vertices);
};
} // namespace Slic3r
#endif // slic3r_NormalUtils_hpp_

View file

@ -517,6 +517,28 @@ bool remove_degenerate(Polylines &polylines)
return modified;
}
std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt)
{
if (polyline.size() < 2) return std::make_pair(-1, Point(0, 0));
auto d2_min = std::numeric_limits<double>::max();
Point foot_pt_min;
Point prev = polyline.front();
auto it = polyline.begin();
auto it_proj = polyline.begin();
for (++it; it != polyline.end(); ++it) {
Point foot_pt = pt.projection_onto(Line(prev, *it));
double d2 = (foot_pt - pt).cast<double>().squaredNorm();
if (d2 < d2_min) {
d2_min = d2;
foot_pt_min = foot_pt;
it_proj = it;
}
prev = *it;
}
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
}
ThickLines ThickPolyline::thicklines() const
{
ThickLines lines;

View file

@ -222,6 +222,9 @@ const Point& leftmost_point(const Polylines &polylines);
bool remove_degenerate(Polylines &polylines);
// Returns index of a segment of a polyline and foot point of pt on polyline.
std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt);
class ThickPolyline : public Polyline {
public:
ThickPolyline() : endpoints(std::make_pair(false, false)) {}

View file

@ -691,7 +691,7 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"chamber_temperature", "nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer",
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
// "bed_type",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq", "slow_down_for_layer_cooling", "fan_min_speed",
@ -1400,7 +1400,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
//filament_id
std::string cloud_filament_id;
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) == preset_values.end()) {
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) {
cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID];
}
@ -1830,9 +1830,12 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select)
{
lock();
auto it = this->find_preset_internal(name);
if (it == m_presets.end() || it->name != name) {
// The preset was not found. Create a new preset.
if (m_presets.begin() + m_idx_selected >= it)
++m_idx_selected;
it = m_presets.emplace(it, Preset(m_type, name, false));
}
Preset &preset = *it;
@ -1842,6 +1845,7 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
preset.is_dirty = false;
if (select)
this->select_preset_by_name(name, true);
unlock();
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", preset type %1%, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_visible;
return preset;

View file

@ -592,6 +592,102 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &config, st
return substitutions;
}
PresetsConfigSubstitutions PresetBundle::import_presets(std::vector<std::string> & files,
std::function<int(std::string const &)> override_confirm,
ForwardCompatibilitySubstitutionRule rule)
{
PresetsConfigSubstitutions substitutions;
int overwrite = 0;
std::vector<std::string> result;
for (auto &file : files) {
if (Slic3r::is_json_file(file)) {
try {
DynamicPrintConfig config;
// BBS: change to json format
// ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ConfigSubstitutions config_substitutions = config.load_from_json(file, rule, key_values, reason);
std::string name = key_values[BBL_JSON_KEY_NAME];
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> version = Semver::parse(version_str);
if (!version) continue;
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
if (version->maj() != app_version.maj()) {
BOOST_LOG_TRIVIAL(warning) << "Preset incompatibla, not loading: " << name;
continue;
}
PresetCollection * collection = nullptr;
if (config.has("printer_settings_id"))
collection = &printers;
else if (config.has("print_settings_id"))
collection = &prints;
else if (config.has("filament_settings_id"))
collection = &filaments;
if (collection == nullptr) {
BOOST_LOG_TRIVIAL(warning) << "Preset type is unknown, not loading: " << name;
continue;
}
if (auto p = collection->find_preset(name, false)) {
if (p->is_default || p->is_system) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present and is system preset, not loading: " << name;
continue;
}
overwrite = override_confirm(name);
if (overwrite == 0 || overwrite == 2) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue;
}
}
DynamicPrintConfig new_config;
Preset * inherit_preset = nullptr;
ConfigOption *inherits_config = config.option(BBL_JSON_KEY_INHERITS);
if (inherits_config) {
ConfigOptionString *option_str = dynamic_cast<ConfigOptionString *>(inherits_config);
std::string inherits_value = option_str->value;
inherit_preset = collection->find_preset(inherits_value, false, true);
}
if (inherit_preset) {
new_config = inherit_preset->config;
} else {
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
// new_config = default_preset.config;
// we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip") % name;
continue;
}
new_config.apply(std::move(config));
Preset &preset = collection->load_preset(collection->path_from_name(name), name, std::move(new_config), false);
preset.is_external = true;
preset.version = *version;
if (inherit_preset)
preset.base_id = inherit_preset->setting_id;
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
const Preset &default_preset = collection->default_preset_for(new_config);
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
if (!incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << preset.file << "\" contains the following incorrect keys: " << incorrect_keys
<< ", which were removed";
if (!config_substitutions.empty())
substitutions.push_back({name, collection->type(), PresetConfigSubstitutions::Source::UserFile, file, std::move(config_substitutions)});
preset.save(inherit_preset ? &inherit_preset->config : nullptr);
result.push_back(file);
} catch (const std::ifstream::failure &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("The config cannot be loaded: %1%. Reason: %2%") % file % err.what();
} catch (const std::runtime_error &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Failed importing config file: %1%. Reason: %2%") % file % err.what();
}
}
}
files = result;
return substitutions;
}
//BBS save user preset to user_id preset folder
void PresetBundle::save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list)
{
@ -626,6 +722,17 @@ void PresetBundle::update_user_presets_directory(const std::string preset_folder
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished");
}
void PresetBundle::remove_user_presets_directory(const std::string preset_folder)
{
const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + preset_folder;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, delete directory : %1%") % dir_user_presets;
fs::path folder(dir_user_presets);
if (fs::exists(folder)) {
fs::remove_all(folder);
}
}
void PresetBundle::update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets)
{
for (auto iterator: system_presets)
@ -3215,6 +3322,36 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri
}
}*/
std::vector<std::string> PresetBundle::export_current_configs(const std::string & path,
std::function<int(std::string const &)> override_confirm,
bool include_modify,
bool export_system_settings)
{
const Preset &print_preset = include_modify ? prints.get_edited_preset() : prints.get_selected_preset();
const Preset &printer_preset = include_modify ? printers.get_edited_preset() : printers.get_selected_preset();
std::set<Preset const *> presets { &print_preset, &printer_preset };
for (auto &f : filament_presets) {
auto filament_preset = filaments.find_preset(f, include_modify);
if (filament_preset) presets.insert(filament_preset);
}
int overwrite = 0;
std::vector<std::string> result;
for (auto preset : presets) {
if ((preset->is_system && !export_system_settings) || preset->is_default)
continue;
std::string file = path + "/" + preset->name + ".json";
if (boost::filesystem::exists(file) && overwrite < 2) {
overwrite = override_confirm(preset->name);
if (overwrite == 0 || overwrite == 2)
continue;
}
preset->config.save_to_json(file, preset->name, "", preset->version.to_string());
result.push_back(file);
}
return result;
}
// Set the filament preset name. As the name could come from the UI selection box,
// an optional "(modified)" suffix will be removed from the filament name.
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)

View file

@ -46,9 +46,11 @@ public:
// BBS Load user presets
PresetsConfigSubstitutions load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule rule);
PresetsConfigSubstitutions import_presets(std::vector<std::string> &files, std::function<int(std::string const &)> override_confirm, ForwardCompatibilitySubstitutionRule rule);
void save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list);
void remove_users_preset(AppConfig &config);
void update_user_presets_directory(const std::string preset_folder);
void update_user_presets_directory(const std::string preset_folder);
void remove_user_presets_directory(const std::string preset_folder);
void update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets);
//BBS: add API to get previous machine
@ -164,6 +166,8 @@ public:
//void export_current_configbundle(const std::string &path);
//BBS: add a function to export system presets for cloud-slicer
//void export_system_configs(const std::string &path);
std::vector<std::string> export_current_configs(const std::string &path, std::function<int(std::string const &)> override_confirm,
bool include_modify, bool export_system_settings = false);
// Enable / disable the "- default -" preset.
void set_default_suppressed(bool default_suppressed);

View file

@ -96,6 +96,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"cool_plate_temp_initial_layer",
"eng_plate_temp_initial_layer",
"hot_plate_temp_initial_layer",
"textured_plate_temp_initial_layer",
"gcode_add_line_number",
"layer_change_gcode",
"fan_min_speed",
@ -175,6 +176,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "cool_plate_temp"
|| opt_key == "eng_plate_temp"
|| opt_key == "hot_plate_temp"
|| opt_key == "textured_plate_temp"
|| opt_key == "enable_prime_tower"
|| opt_key == "prime_tower_width"
|| opt_key == "prime_tower_brim_width"
@ -1132,6 +1134,46 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
}
}
void PrintObject::set_shared_object(PrintObject *object)
{
m_shared_object = object;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, found shared object from %2%")%this%m_shared_object;
}
void PrintObject::clear_shared_object()
{
if (m_shared_object) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object;
m_layers.clear();
m_support_layers.clear();
m_tree_support_layers.clear();
m_shared_object = nullptr;
invalidate_all_steps_without_cancel();
}
}
void PrintObject::copy_layers_from_shared_object()
{
if (m_shared_object) {
m_layers.clear();
m_support_layers.clear();
m_tree_support_layers.clear();
firstLayerObjSliceByVolume.clear();
firstLayerObjSliceByGroups.clear();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object;
m_layers = m_shared_object->layers();
m_support_layers = m_shared_object->support_layers();
m_tree_support_layers = m_shared_object->tree_support_layers();
firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice();
firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups();
}
}
// BBS
BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std::string& name)
{
@ -1181,15 +1223,115 @@ void Print::process()
{
name_tbb_thread_pool_threads_set_locale();
//compute the PrintObject with the same geometries
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter")%this;
for (PrintObject *obj : m_objects)
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;
const ModelObject* model_obj1 = object1->model_object();
const ModelObject* model_obj2 = object2->model_object();
if (model_obj1->volumes.size() != model_obj2->volumes.size())
return false;
bool has_extruder1 = model_obj1->config.has("extruder");
bool has_extruder2 = model_obj2->config.has("extruder");
if ((has_extruder1 != has_extruder2)
|| (has_extruder1 && model_obj1->config.extruder() != model_obj2->config.extruder()))
return false;
for (int index = 0; index < model_obj1->volumes.size(); index++) {
const ModelVolume &model_volume1 = *model_obj1->volumes[index];
const ModelVolume &model_volume2 = *model_obj2->volumes[index];
if (model_volume1.type() != model_volume2.type())
return false;
if (model_volume1.mesh_ptr() != model_volume2.mesh_ptr())
return false;
has_extruder1 = model_volume1.config.has("extruder");
has_extruder2 = model_volume2.config.has("extruder");
if ((has_extruder1 != has_extruder2)
|| (has_extruder1 && model_volume1.config.extruder() != model_volume2.config.extruder()))
return false;
if (!model_volume1.supported_facets.equals(model_volume2.supported_facets))
return false;
if (!model_volume1.seam_facets.equals(model_volume2.seam_facets))
return false;
if (!model_volume1.mmu_segmentation_facets.equals(model_volume2.mmu_segmentation_facets))
return false;
if (model_volume1.config.get() != model_volume2.config.get())
return false;
}
//if (!object1->config().equals(object2->config()))
// return false;
if (model_obj1->config.get() != model_obj2->config.get())
return false;
return true;
};
int object_count = m_objects.size();
std::set<PrintObject*> need_slicing_objects;
for (int index = 0; index < object_count; index++)
{
PrintObject *obj = m_objects[index];
for (PrintObject *slicing_obj : need_slicing_objects)
{
if (is_print_object_the_same(obj, slicing_obj)) {
obj->set_shared_object(slicing_obj);
break;
}
}
if (!obj->get_shared_object())
need_slicing_objects.insert(obj);
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->make_perimeters();
}
else {
if (obj->set_started(posSlice))
obj->set_done(posSlice);
if (obj->set_started(posPerimeters))
obj->set_done(posPerimeters);
}
}
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->infill();
}
else {
if (obj->set_started(posPrepareInfill))
obj->set_done(posPrepareInfill);
if (obj->set_started(posInfill))
obj->set_done(posInfill);
}
}
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->ironing();
}
else {
if (obj->set_started(posIroning))
obj->set_done(posIroning);
}
}
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->generate_support_material();
}
else {
if (obj->set_started(posSupportMaterial))
obj->set_done(posSupportMaterial);
}
}
for (PrintObject *obj : m_objects)
obj->make_perimeters();
for (PrintObject *obj : m_objects)
obj->infill();
for (PrintObject *obj : m_objects)
obj->ironing();
for (PrintObject *obj : m_objects)
obj->generate_support_material();
{
if (need_slicing_objects.count(obj) == 0)
obj->copy_layers_from_shared_object();
}
if (this->set_started(psWipeTower)) {
m_wipe_tower_data.clear();
m_tool_ordering.clear();
@ -1292,8 +1434,17 @@ void Print::process()
this->set_done(psSkirtBrim);
}
//BBS
for (PrintObject *obj : m_objects)
obj->simplify_extrusion_path();
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->simplify_extrusion_path();
}
else {
if (obj->set_started(posSimplifyPath))
obj->set_done(posSimplifyPath);
if (obj->set_started(posSimplifySupportPath))
obj->set_done(posSimplifySupportPath);
}
}
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
}
@ -1634,7 +1785,7 @@ void Print::_make_wipe_tower()
for (LayerTools& layer_tools : layer_tools_array) {
layer_tools.has_wipe_tower = true;
if (layer_tools.wipe_tower_partitions == 0) {
layer_tools.wipe_tower_partitions = 1;
layer_tools.wipe_tower_partitions = 1;
}
}
}

View file

@ -49,6 +49,12 @@ struct groupedVolumeSlices
ExPolygons slices;
};
enum SupportNecessaryType {
NoNeedSupp=0,
SharpTail,
LargeOverhang,
};
namespace FillAdaptive {
struct Octree;
struct OctreeDeleter;
@ -409,6 +415,11 @@ public:
//BBS
BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name);
PrintObject* get_shared_object() const { return m_shared_object; }
void set_shared_object(PrintObject *object);
void clear_shared_object();
void copy_layers_from_shared_object();
// BBS: Boundingbox of the first layer
BoundingBox firstLayerObjectBrimBoundingBox;
private:
@ -457,7 +468,7 @@ private:
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data();
// BBS
bool is_support_necessary();
SupportNecessaryType is_support_necessary();
// XYZ in scaled coordinates
Vec3crd m_size;
@ -489,6 +500,8 @@ private:
// BBS: per object skirt
ExtrusionEntityCollection m_skirt;
PrintObject* m_shared_object{ nullptr };
public:
//BBS: When printing multi-material objects, this settings will make slicer to clip the overlapping object parts one by the other.
//(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc).

View file

@ -629,6 +629,8 @@ protected:
{ return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_callback(m_print)); }
bool invalidate_all_steps()
{ return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); }
bool invalidate_all_steps_without_cancel()
{ return m_state.invalidate_all([](){}); }
bool is_step_started_unguarded(PrintObjectStepEnum step) const { return m_state.is_started_unguarded(step); }
bool is_step_done_unguarded(PrintObjectStepEnum step) const { return m_state.is_done_unguarded(step); }

View file

@ -230,7 +230,8 @@ static const t_config_enum_values s_keys_map_OverhangFanThreshold = {
static const t_config_enum_values s_keys_map_BedType = {
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI }
{ "High Temp Plate", btPEI },
{ "Textured PEI Plate", btPTE }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType)
@ -462,6 +463,16 @@ void PrintConfigDef::init_fff_params()
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 45 });
def = this->add("textured_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
"Value 0 means the filament does not support to print on the Textured PEI Plate");
def->sidetext = L("°C");
def->full_label = L("Bed temperature");
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
@ -492,6 +503,15 @@ void PrintConfigDef::init_fff_params()
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 45 });
def = this->add("textured_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
"Value 0 means the filament does not support to print on the Textured PEI Plate");
def->sidetext = L("°C");
def->max = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("curr_bed_type", coEnums);
def->label = L("Bed type");
@ -501,9 +521,11 @@ void PrintConfigDef::init_fff_params()
def->enum_values.emplace_back("Cool Plate");
def->enum_values.emplace_back("Engineering Plate");
def->enum_values.emplace_back("High Temp Plate");
def->enum_values.emplace_back("Textured PEI Plate");
def->enum_labels.emplace_back(L("Cool Plate"));
def->enum_labels.emplace_back(L("Engineering Plate"));
def->enum_labels.emplace_back(L("High Temp Plate"));
def->enum_labels.emplace_back(L("Textured PEI Plate"));
def->set_default_value(new ConfigOptionEnum<BedType>(btPC));
def = this->add("before_layer_change_gcode", coString);
@ -1091,7 +1113,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Filament price. For statistics only");
def->sidetext = L("money/kg");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
def = this->add("filament_settings_id", coStrings);
@ -2000,9 +2022,11 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("nearest");
def->enum_values.push_back("aligned");
def->enum_values.push_back("back");
def->enum_values.push_back("random");
def->enum_labels.push_back(L("Nearest"));
def->enum_labels.push_back(L("Aligned"));
def->enum_labels.push_back(L("Back"));
def->enum_labels.push_back(L("Random"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<SeamPosition>(spAligned));
@ -2108,10 +2132,12 @@ void PrintConfigDef::init_fff_params()
def = this->add("timelapse_no_toolhead", coBool);
def->label = L("Timelapse");
def->tooltip = L("Record timelapse video of printing without showing toolhead. In this mode "
"the toolhead docks near the excess chute at each layer change, and then "
"a snapshot is taken with the chamber camera. When printing finishes a timelapse "
"video is composed of all the snapshots.");
def->tooltip = L("If enabled, a timelapse video will be generated for each print. "
"After each layer is printed, the toolhead will move to the excess chute, "
"and then a snapshot is taken with the chamber camera. "
"All of these snapshots are composed into a timelapse video when printing completes. "
"Since the melt filament may leak from the nozzle during the process of taking a snapshot, "
"prime tower is required for nozzle priming.");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
@ -2406,8 +2432,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("independent_support_layer_height", coBool);
def->label = L("Independent support layer height");
def->category = L("Support");
def->tooltip = L("Support layer uses layer height independent with object layer. This is to support custom support gap,"
"but may cause extra filament switches if support is specified as different extruder with object");
def->tooltip = L("Support layer uses layer height independent with object layer. This is to support customizing z-gap and save print time.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));

View file

@ -160,6 +160,7 @@ enum BedType {
btPC = 0,
btEP,
btPEI,
btPTE,
btCount
};
@ -185,6 +186,8 @@ static std::string bed_type_to_gcode_string(const BedType type)
case btPEI:
type_str = "high_temp_plate";
break;
case btPTE:
type_str = "frosted_plate";
default:
type_str = "unknown";
break;
@ -204,6 +207,9 @@ static std::string get_bed_temp_key(const BedType type)
if (type == btPEI)
return "hot_plate_temp";
if (type == btPTE)
return "textured_plate_temp";
return "";
}
@ -218,6 +224,9 @@ static std::string get_bed_temp_1st_layer_key(const BedType type)
if (type == btPEI)
return "hot_plate_temp_initial_layer";
if (type == btPTE)
return "textured_plate_temp_initial_layer";
return "";
}
@ -786,9 +795,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionInts, cool_plate_temp))
((ConfigOptionInts, eng_plate_temp))
((ConfigOptionInts, hot_plate_temp)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp))
((ConfigOptionInts, cool_plate_temp_initial_layer))
((ConfigOptionInts, eng_plate_temp_initial_layer))
((ConfigOptionInts, hot_plate_temp_initial_layer)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp_initial_layer))
((ConfigOptionBools, enable_overhang_bridge_fan))
((ConfigOptionInts, overhang_fan_speed))
((ConfigOptionEnumsGeneric, overhang_fan_threshold))

View file

@ -86,6 +86,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
PrintObject::~PrintObject()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, m_shared_object %2%")%this%m_shared_object;
if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions;
clear_layers();
clear_support_layers();
@ -419,11 +420,17 @@ void PrintObject::generate_support_material()
m_print->throw_if_canceled();
} else {
// BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled
if (this->is_support_necessary()) {
SupportNecessaryType sntype = this->is_support_necessary();
if (sntype != NoNeedSupp) {
m_print->set_status(50, L("Checking support necessity"));
std::string warning_message = format(L("It seems object %s needs support to print. Please enable support generation."), this->model_object()->name);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
if (sntype == SharpTail) {
std::string warning_message = format(L("It seems object %s has completely floating regions. Please re-orient the object or enable support generation."),
this->model_object()->name);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
} else {
std::string warning_message = format(L("It seems object %s has large overhangs. Please enable support generation."), this->model_object()->name);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
}
}
#if 0
@ -529,9 +536,11 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
void PrintObject::clear_layers()
{
for (Layer *l : m_layers)
delete l;
m_layers.clear();
if (!m_shared_object) {
for (Layer *l : m_layers)
delete l;
m_layers.clear();
}
}
Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
@ -567,9 +576,11 @@ SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_
void PrintObject::clear_tree_support_layers()
{
for (TreeSupportLayer* l : m_tree_support_layers)
delete l;
m_tree_support_layers.clear();
if (!m_shared_object) {
for (TreeSupportLayer* l : m_tree_support_layers)
delete l;
m_tree_support_layers.clear();
}
}
std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
@ -596,9 +607,11 @@ TreeSupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, c
void PrintObject::clear_support_layers()
{
for (Layer *l : m_support_layers)
delete l;
m_support_layers.clear();
if (!m_shared_object) {
for (Layer *l : m_support_layers)
delete l;
m_support_layers.clear();
}
}
SupportLayer* PrintObject::add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z)
@ -2448,7 +2461,7 @@ template void PrintObject::remove_bridges_from_contacts<Polygons>(
float max_bridge_length, bool break_bridge);
bool PrintObject::is_support_necessary()
SupportNecessaryType PrintObject::is_support_necessary()
{
static const double super_overhang_area_threshold = SQ(scale_(5.0));
@ -2471,7 +2484,7 @@ bool PrintObject::is_support_necessary()
for (const ExPolygon& expoly : layerm->raw_slices) {
// detect sharp tail
if (intersection_ex({ expoly }, lower_layer_offseted).empty())
return true;
return SharpTail;
}
}
@ -2503,18 +2516,19 @@ bool PrintObject::is_support_necessary()
double super_overhang_area = 0.0;
for (Polygon& poly : super_overhang_polys) {
bool is_ccw = poly.is_counter_clockwise();
double area_ = poly.area();
if (is_ccw) {
if (super_overhang_area > super_overhang_area_threshold)
return true;
super_overhang_area = poly.area();
if (area_ > super_overhang_area_threshold)
return LargeOverhang;
super_overhang_area += area_;
}
else {
super_overhang_area -= poly.area();
super_overhang_area -= area_;
}
}
if (super_overhang_area > super_overhang_area_threshold)
return true;
//if (super_overhang_area > super_overhang_area_threshold)
// return LargeOverhang;
// 3. check overhang distance
const double distance_threshold_scaled = extrusion_width_scaled * 2;
@ -2529,10 +2543,10 @@ bool PrintObject::is_support_necessary()
}),
exceed_overhang.end());
if (!exceed_overhang.empty())
return true;
return LargeOverhang;
}
return false;
return NoNeedSupp;
}
static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector<Polygons> &out)

View file

@ -474,9 +474,7 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
if (layers[idx_layer]->slicing_errors) {
buggy_layers.push_back(idx_layer);
//BBS
error_msg = L("Empty layers around bottom are replaced by nearest normal layers.");
}
}
else
break; // only detect empty layers near bed
}
@ -484,14 +482,15 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, buggy_layers.size()),
[&layers, &throw_if_canceled, &buggy_layers](const tbb::blocked_range<size_t>& range) {
[&layers, &throw_if_canceled, &buggy_layers, &error_msg](const tbb::blocked_range<size_t>& range) {
for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) {
throw_if_canceled();
size_t idx_layer = buggy_layers[buggy_layer_idx];
// BBS: only replace empty first layer
if (idx_layer > 0)
// BBS: only replace empty layers lower than 1mm
const coordf_t thresh_empty_layer_height = 1;
Layer* layer = layers[idx_layer];
if (layer->print_z>= thresh_empty_layer_height)
continue;
Layer *layer = layers[idx_layer];
assert(layer->slicing_errors);
// Try to repair the layer surfaces by merging all contours and all holes from neighbor layers.
// BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer;
@ -500,42 +499,39 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
// Find the first valid layer below / above the current layer.
const Surfaces *upper_surfaces = nullptr;
const Surfaces *lower_surfaces = nullptr;
//BBS: only repair first layer if the 2nd layer is Good
for (size_t j = idx_layer + 1; j < /*layers.size()*/2; ++ j)
if (! layers[j]->slicing_errors) {
//BBS: only repair empty layers lowers than 1mm
for (size_t j = idx_layer + 1; j < layers.size(); ++j) {
if (!layers[j]->slicing_errors) {
upper_surfaces = &layers[j]->regions()[region_id]->slices.surfaces;
break;
}
for (int j = /*int(idx_layer) -*/ 1; j >= 0; -- j)
if (! layers[j]->slicing_errors) {
if (layers[j]->print_z >= thresh_empty_layer_height) break;
}
for (int j = int(idx_layer) - 1; j >= 0; --j) {
if (layers[j]->print_z >= thresh_empty_layer_height) continue;
if (!layers[j]->slicing_errors) {
lower_surfaces = &layers[j]->regions()[region_id]->slices.surfaces;
break;
}
}
// Collect outer contours and holes from the valid layers above & below.
Polygons outer;
outer.reserve(
ExPolygons expolys;
expolys.reserve(
((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) +
((lower_surfaces == nullptr) ? 0 : lower_surfaces->size()));
size_t num_holes = 0;
if (upper_surfaces)
for (const auto &surface : *upper_surfaces) {
outer.push_back(surface.expolygon.contour);
num_holes += surface.expolygon.holes.size();
expolys.emplace_back(surface.expolygon);
}
if (lower_surfaces)
for (const auto &surface : *lower_surfaces) {
outer.push_back(surface.expolygon.contour);
num_holes += surface.expolygon.holes.size();
expolys.emplace_back(surface.expolygon);
}
Polygons holes;
holes.reserve(num_holes);
if (upper_surfaces)
for (const auto &surface : *upper_surfaces)
polygons_append(holes, surface.expolygon.holes);
if (lower_surfaces)
for (const auto &surface : *lower_surfaces)
polygons_append(holes, surface.expolygon.holes);
layerm->slices.set(diff_ex(union_(outer), holes), stInternal);
if (!expolys.empty()) {
//BBS
error_msg = L("Empty layers around bottom are replaced by nearest normal layers.");
layerm->slices.set(union_ex(expolys), stInternal);
}
}
// Update layer slices after repairing the single regions.
layer->make_slices();
@ -555,8 +551,7 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
//BBS
if(error_msg.empty() && !buggy_layers.empty())
error_msg = L("The model has overlapping or self-intersecting facets. I tried to repair it, "
"however you might want to check the results or repair the input file and retry.");
error_msg = L("The model has too many empty layers.");
return error_msg;
}

View file

@ -51,6 +51,7 @@ public:
{
profile_ = profile;
prediction = 0;
weight = 0.0f;
}
BBLSliceInfo(const BBLSliceInfo& obj) {

View file

@ -0,0 +1,203 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../TriangleMesh.hpp"
#include "TextShape.hpp"
#include <string>
#include <vector>
#include "Standard_TypeDef.hxx"
#include "STEPCAFControl_Reader.hxx"
#include "BRepMesh_IncrementalMesh.hxx"
#include "Interface_Static.hxx"
#include "XCAFDoc_DocumentTool.hxx"
#include "XCAFDoc_ShapeTool.hxx"
#include "XCAFApp_Application.hxx"
#include "TopoDS_Solid.hxx"
#include "TopoDS_Compound.hxx"
#include "TopoDS_Builder.hxx"
#include "TopoDS.hxx"
#include "TDataStd_Name.hxx"
#include "BRepBuilderAPI_Transform.hxx"
#include "TopExp_Explorer.hxx"
#include "TopExp_Explorer.hxx"
#include "BRep_Tool.hxx"
#include "Font_BRepFont.hxx"
#include "Font_BRepTextBuilder.hxx"
#include "BRepPrimAPI_MakePrism.hxx"
#include "Font_FontMgr.hxx"
namespace Slic3r {
std::vector<std::string> init_occt_fonts()
{
std::vector<std::string> stdFontNames;
Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
aFontMgr->InitFontDataBase();
TColStd_SequenceOfHAsciiString availFontNames;
aFontMgr->GetAvailableFontsNames(availFontNames);
stdFontNames.reserve(availFontNames.Size());
for (auto afn : availFontNames)
stdFontNames.push_back(afn->ToCString());
return stdFontNames;
}
static bool TextToBRep(const char* text, const char* font, const float theTextHeight, Font_FontAspect& theFontAspect, TopoDS_Shape& theShape)
{
Standard_Integer anArgIt = 1;
Standard_CString aName = "text_shape";
Standard_CString aText = text;
Font_BRepFont aFont;
//TCollection_AsciiString aFontName("Courier");
TCollection_AsciiString aFontName(font);
Standard_Real aTextHeight = theTextHeight;
Font_FontAspect aFontAspect = theFontAspect;
Standard_Boolean anIsCompositeCurve = Standard_False;
gp_Ax3 aPenAx3(gp::XOY());
gp_Dir aNormal(0.0, 0.0, 1.0);
gp_Dir aDirection(1.0, 0.0, 0.0);
gp_Pnt aPenLoc;
Graphic3d_HorizontalTextAlignment aHJustification = Graphic3d_HTA_LEFT;
Graphic3d_VerticalTextAlignment aVJustification = Graphic3d_VTA_BOTTOM;
Font_StrictLevel aStrictLevel = Font_StrictLevel_Any;
aFont.SetCompositeCurveMode(anIsCompositeCurve);
if (!aFont.FindAndInit(aFontName.ToCString(), aFontAspect, aTextHeight, aStrictLevel))
return false;
aPenAx3 = gp_Ax3(aPenLoc, aNormal, aDirection);
Font_BRepTextBuilder aBuilder;
theShape = aBuilder.Perform(aFont, aText, aPenAx3, aHJustification, aVJustification);
return true;
}
static bool Prism(const TopoDS_Shape& theBase, const float thickness, TopoDS_Shape& theSolid)
{
if (theBase.IsNull()) return false;
gp_Vec V(0.f, 0.f, thickness);
BRepPrimAPI_MakePrism* Prism = new BRepPrimAPI_MakePrism(theBase, V, Standard_False);
theSolid = Prism->Shape();
return true;
}
static void MakeMesh(TopoDS_Shape& theSolid, TriangleMesh& theMesh)
{
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
BRepMesh_IncrementalMesh mesh(theSolid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(theSolid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
stl_file stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)aNbTriangles;
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
//BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
//BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(theSolid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape& aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
//BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
//BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
Standard_Integer anId[3];
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED) {
//BBS: swap 1, 2.
Standard_Integer aTmpIdx = anId[1];
anId[1] = anId[2];
anId[2] = aTmpIdx;
}
//BBS: Update nodes according to the offset.
anId[0] += aNodeOffset;
anId[1] += aNodeOffset;
anId[2] += aNodeOffset;
//BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] - 1].cast<float>();
facet.vertex[1] = points[anId[1] - 1].cast<float>();
facet.vertex[2] = points[anId[2] - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
theMesh.from_stl(stl);
}
void load_text_shape(const char*text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh)
{
Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
if (aFontMgr->GetAvailableFonts().IsEmpty())
aFontMgr->InitFontDataBase();
TopoDS_Shape aTextBase;
Font_FontAspect aFontAspect = Font_FontAspect_UNDEFINED;
if (is_bold && is_italic)
aFontAspect = Font_FontAspect_BoldItalic;
else if (is_bold)
aFontAspect = Font_FontAspect_Bold;
else if (is_italic)
aFontAspect = Font_FontAspect_Italic;
else
aFontAspect = Font_FontAspect_Regular;
if (!TextToBRep(text, font, text_height, aFontAspect, aTextBase))
return;
TopoDS_Shape aTextShape;
if (!Prism(aTextBase, thickness, aTextShape))
return;
MakeMesh(aTextShape, text_mesh);
}
}; // namespace Slic3r

View file

@ -0,0 +1,12 @@
#ifndef slic3r_Text_Shape_hpp_
#define slic3r_Text_Shape_hpp_
namespace Slic3r {
class TriangleMesh;
extern std::vector<std::string> init_occt_fonts();
extern void load_text_shape(const char* text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh);
}; // namespace Slic3r
#endif // slic3r_Text_Shape_hpp_

View file

@ -0,0 +1,183 @@
#include "ShortEdgeCollapse.hpp"
#include "libslic3r/NormalUtils.hpp"
#include <unordered_map>
#include <unordered_set>
#include <random>
#include <algorithm>
namespace Slic3r {
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) {
// whenever vertex is removed, its mapping is update to the index of vertex with wich it merged
std::vector<size_t> vertices_index_mapping(mesh.vertices.size());
for (size_t idx = 0; idx < vertices_index_mapping.size(); ++idx) {
vertices_index_mapping[idx] = idx;
}
// Algorithm uses get_final_index query to get the actual vertex index. The query also updates all mappings on the way, essentially flattening the mapping
std::vector<size_t> flatten_queue;
auto get_final_index = [&vertices_index_mapping, &flatten_queue](const size_t &orig_index) {
flatten_queue.clear();
size_t idx = orig_index;
while (vertices_index_mapping[idx] != idx) {
flatten_queue.push_back(idx);
idx = vertices_index_mapping[idx];
}
for (size_t i : flatten_queue) {
vertices_index_mapping[i] = idx;
}
return idx;
};
// if face is removed, mark it here
std::vector<bool> face_removal_flags(mesh.indices.size(), false);
std::vector<Vec3i> triangles_neighbors = its_face_neighbors_par(mesh);
// now compute vertices dot product - this is used during edge collapse,
// to determine which vertex to remove and which to keep; We try to keep the one with larger angle, because it defines the shape "more".
// The min vertex dot product is lowest dot product of its normal with the normals of faces around it.
// the lower the dot product, the more we want to keep the vertex
// NOTE: This score is not updated, even though the decimation does change the mesh. It saves computation time, and there are no strong reasons to update.
std::vector<float> min_vertex_dot_product(mesh.vertices.size(), 1);
{
std::vector<Vec3f> face_normals = its_face_normals(mesh);
std::vector<Vec3f> vertex_normals = NormalUtils::create_normals(mesh);
for (size_t face_idx = 0; face_idx < mesh.indices.size(); ++face_idx) {
Vec3i t = mesh.indices[face_idx];
Vec3f n = face_normals[face_idx];
min_vertex_dot_product[t[0]] = std::min(min_vertex_dot_product[t[0]], n.dot(vertex_normals[t[0]]));
min_vertex_dot_product[t[1]] = std::min(min_vertex_dot_product[t[1]], n.dot(vertex_normals[t[1]]));
min_vertex_dot_product[t[2]] = std::min(min_vertex_dot_product[t[2]], n.dot(vertex_normals[t[2]]));
}
}
// lambda to remove face. It flags the face as removed, and updates neighbourhood info
auto remove_face = [&triangles_neighbors, &face_removal_flags](int face_idx, int other_face_idx) {
if (face_idx < 0) {
return;
}
face_removal_flags[face_idx] = true;
Vec3i neighbors = triangles_neighbors[face_idx];
int n_a = neighbors[0] != other_face_idx ? neighbors[0] : neighbors[1];
int n_b = neighbors[2] != other_face_idx ? neighbors[2] : neighbors[1];
if (n_a > 0)
for (int &n : triangles_neighbors[n_a]) {
if (n == face_idx) {
n = n_b;
break;
}
}
if (n_b > 0)
for (int &n : triangles_neighbors[n_b]) {
if (n == face_idx) {
n = n_a;
break;
}
}
};
std::mt19937_64 generator { 27644437 };// default constant seed! so that results are deterministic
std::vector<size_t> face_indices(mesh.indices.size());
for (size_t idx = 0; idx < face_indices.size(); ++idx) {
face_indices[idx] = idx;
}
//tmp face indices used only for swapping
std::vector<size_t> tmp_face_indices(mesh.indices.size());
float decimation_ratio = 1.0f; // decimation ratio updated in each iteration. it is number of removed triangles / number of all
float edge_len = 0.2f; // Allowed collapsible edge size. Starts low, but is gradually increased
while (face_indices.size() > target_triangle_count) {
// simpple func to increase the edge len - if decimation ratio is low, it increases the len up to twice, if decimation ratio is high, increments are low
edge_len = edge_len * (1.0f + 1.0 - decimation_ratio);
float max_edge_len_squared = edge_len * edge_len;
//shuffle the faces and traverse in random order, this MASSIVELY improves the quality of the result
std::shuffle(face_indices.begin(), face_indices.end(), generator);
for (const size_t &face_idx : face_indices) {
if (face_removal_flags[face_idx]) {
// if face already removed from previous collapses, skip (each collapse removes two triangles [at least] )
continue;
}
// look at each edge if it is good candidate for collapse
for (size_t edge_idx = 0; edge_idx < 3; ++edge_idx) {
size_t vertex_index_keep = get_final_index(mesh.indices[face_idx][edge_idx]);
size_t vertex_index_remove = get_final_index(mesh.indices[face_idx][(edge_idx + 1) % 3]);
//check distance, skip long edges
if ((mesh.vertices[vertex_index_keep] - mesh.vertices[vertex_index_remove]).squaredNorm()
> max_edge_len_squared) {
continue;
}
// swap indexes if vertex_index_keep has higher dot product (we want to keep low dot product vertices)
if (min_vertex_dot_product[vertex_index_remove] < min_vertex_dot_product[vertex_index_keep]) {
size_t tmp = vertex_index_keep;
vertex_index_keep = vertex_index_remove;
vertex_index_remove = tmp;
}
//remove vertex
{
// map its index to the index of the kept vertex
vertices_index_mapping[vertex_index_remove] = vertices_index_mapping[vertex_index_keep];
}
int neighbor_to_remove_face_idx = triangles_neighbors[face_idx][edge_idx];
// remove faces
remove_face(face_idx, neighbor_to_remove_face_idx);
remove_face(neighbor_to_remove_face_idx, face_idx);
// break. this triangle is done
break;
}
}
// filter face_indices, remove those that have been collapsed
size_t prev_size = face_indices.size();
tmp_face_indices.clear();
for (size_t face_idx : face_indices) {
if (!face_removal_flags[face_idx]){
tmp_face_indices.push_back(face_idx);
}
}
face_indices.swap(tmp_face_indices);
decimation_ratio = float(prev_size - face_indices.size()) / float(prev_size);
//std::cout << " DECIMATION RATIO: " << decimation_ratio << std::endl;
}
//Extract the result mesh
std::unordered_map<size_t, size_t> final_vertices_mapping;
std::vector<Vec3f> final_vertices;
std::vector<Vec3i> final_indices;
final_indices.reserve(face_indices.size());
for (size_t idx : face_indices) {
Vec3i final_face;
for (size_t i = 0; i < 3; ++i) {
final_face[i] = get_final_index(mesh.indices[idx][i]);
}
if (final_face[0] == final_face[1] || final_face[1] == final_face[2] || final_face[2] == final_face[0]) {
continue; // discard degenerate triangles
}
for (size_t i = 0; i < 3; ++i) {
if (final_vertices_mapping.find(final_face[i]) == final_vertices_mapping.end()) {
final_vertices_mapping[final_face[i]] = final_vertices.size();
final_vertices.push_back(mesh.vertices[final_face[i]]);
}
final_face[i] = final_vertices_mapping[final_face[i]];
}
final_indices.push_back(final_face);
}
mesh.vertices = final_vertices;
mesh.indices = final_indices;
}
} //namespace Slic3r

View file

@ -0,0 +1,16 @@
#ifndef SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_
#define SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_
#include "libslic3r/TriangleMesh.hpp"
namespace Slic3r{
// Decimates the model by collapsing short edges. It starts with very small edges and gradually increases the collapsible length,
// until the target triangle count is reached (the algorithm will certainly undershoot the target count, result will have less triangles than target count)
// The algorithm does not check for triangle flipping, disconnections, self intersections or any other degeneration that can appear during mesh processing.
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count);
}
#endif /* SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_ */

View file

@ -1421,6 +1421,15 @@ void TreeSupport::generate_toolpaths()
bool obj_is_vertical = obj_size.x() < obj_size.y();
int num_layers_to_change_infill_direction = int(HEIGHT_TO_SWITCH_INFILL_DIRECTION / object_config.layer_height.value); // change direction every 30mm
std::shared_ptr<Fill> filler_interface = std::shared_ptr<Fill>(Fill::new_from_type(m_support_params.contact_fill_pattern));
std::shared_ptr<Fill> filler_Roof1stLayer = std::shared_ptr<Fill>(Fill::new_from_type(ipRectilinear));
std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_Roof1stLayer->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);//(1 - obj_is_vertical) * M_PI_2;//((1-obj_is_vertical) + int(layer_id / num_layers_to_change_infill_direction)) * M_PI_2;;//layer_id % 2 ? 0 : M_PI_2;
filler_Roof1stLayer->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);
// generate tree support tool paths
tbb::parallel_for(
tbb::blocked_range<size_t>(m_raft_layers, m_object->tree_support_layer_count()),
@ -1434,12 +1443,7 @@ void TreeSupport::generate_toolpaths()
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_id);
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
std::unique_ptr<Fill> filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.contact_fill_pattern));
std::unique_ptr<Fill> filler_support = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);//(1 - obj_is_vertical) * M_PI_2;//((1-obj_is_vertical) + int(layer_id / num_layers_to_change_infill_direction)) * M_PI_2;;//layer_id % 2 ? 0 : M_PI_2;
for (auto& area_group : ts_layer->area_groups) {
ExPolygon& poly = *area_group.first;
@ -1465,9 +1469,9 @@ void TreeSupport::generate_toolpaths()
// roof_1st_layer
fill_params.density = interface_density;
// Note: spacing means the separation between two lines as if they are tightly extruded
filler_interface->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_interface.get(), fill_params, erSupportMaterial,
m_support_material_interface_flow);
filler_Roof1stLayer->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_Roof1stLayer.get(), fill_params, erSupportMaterial,
m_support_material_interface_flow);
} else if (area_group.second == TreeSupportLayer::FloorType) {
// floor_areas
fill_params.density = bottom_interface_density;
@ -2670,7 +2674,7 @@ void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_
for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
for (Node* node : curr_layer_nodes) {
if (node->support_roof_layers_below == top_intf_layers || node->support_floor_layers_above == bot_intf_layers) {
if (node->support_roof_layers_below >0 || node->support_floor_layers_above == bot_intf_layers) {
extremes.push_back(layer_nr);
break;
}

View file

@ -9,7 +9,7 @@
#include "Execution/ExecutionTBB.hpp"
#include "Execution/ExecutionSeq.hpp"
#include "Utils.hpp"
#include "Format/STL.hpp"
#include <libqhullcpp/Qhull.h>
#include <libqhullcpp/QhullFacetList.h>
#include <libqhullcpp/QhullVertexSet.h>
@ -208,10 +208,10 @@ bool TriangleMesh::from_stl(stl_file& stl, bool repair)
return true;
}
bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair)
bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair, ImportstlProgressFn stlFn)
{
stl_file stl;
if (! stl_open(&stl, input_file))
if (! stl_open(&stl, input_file, stlFn))
return false;
return from_stl(stl, repair);
}

View file

@ -10,7 +10,7 @@
#include "Point.hpp"
#include "Polygon.hpp"
#include "ExPolygon.hpp"
#include "Format/STL.hpp"
namespace Slic3r {
class TriangleMesh;
@ -94,7 +94,7 @@ public:
explicit TriangleMesh(indexed_triangle_set &&M, const RepairedMeshErrors& repaired_errors = RepairedMeshErrors());
void clear() { this->its.clear(); this->m_stats.clear(); }
bool from_stl(stl_file& stl, bool repair = true);
bool ReadSTLFile(const char* input_file, bool repair = true);
bool ReadSTLFile(const char* input_file, bool repair = true, ImportstlProgressFn stlFn = nullptr);
bool write_ascii(const char* output_file);
bool write_binary(const char* output_file);
float volume();
@ -152,10 +152,14 @@ public:
const TriangleMeshStats& stats() const { return m_stats; }
void set_init_shift(const Vec3d &offset) { m_init_shift = offset; }
Vec3d get_init_shift() const { return m_init_shift; }
indexed_triangle_set its;
private:
TriangleMeshStats m_stats;
Vec3d m_init_shift {0.0, 0.0, 0.0};
};
// Index of face indices incident with a vertex index.

View file

@ -1427,7 +1427,7 @@ void TriangleSelector::get_facets(std::vector<indexed_triangle_set>& facets_per_
{
facets_per_type.clear();
for (int type = (int)EnforcerBlockerType::NONE; type < (int)EnforcerBlockerType::Extruder15; type++) {
for (int type = (int)EnforcerBlockerType::NONE; type <= (int)EnforcerBlockerType::ExtruderMax; type++) {
facets_per_type.emplace_back();
indexed_triangle_set& its = facets_per_type.back();
std::vector<int> vertex_map(m_vertices.size(), -1);

View file

@ -7,12 +7,10 @@
#include <cfloat>
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r {
enum class EnforcerBlockerType : int8_t;
// Following class holds information about selected triangles. It also has power
// to recursively subdivide the triangles and make the selection finer.
class TriangleSelector
@ -275,7 +273,7 @@ public:
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> serialize() const;
// Load serialized data. Assumes that correct mesh is loaded.
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data, bool needs_reset = true, EnforcerBlockerType max_ebt = (EnforcerBlockerType)std::numeric_limits<int8_t>::max());
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data, bool needs_reset = true, EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax);
// For all triangles, remove the flag indicating that the triangle was selected by seed fill.
void seed_fill_unselect_all_triangles();
@ -312,7 +310,7 @@ protected:
void set_division(int sides_to_split, int special_side_idx);
// Get/set current state.
void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; }
void set_state(EnforcerBlockerType type) { assert(!is_split()); state = type; }
EnforcerBlockerType get_state() const { assert(! is_split()); return state; }
// Set if the triangle has been selected or unselected by seed fill.

View file

@ -0,0 +1,71 @@
#include "TriangleSetSampling.hpp"
#include <map>
#include <random>
#include <tbb/parallel_for.h>
#include <tbb/blocked_range.h>
namespace Slic3r {
TriangleSetSamples sample_its_uniform_parallel(size_t samples_count, const indexed_triangle_set &triangle_set) {
std::vector<double> triangles_area(triangle_set.indices.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, triangle_set.indices.size()),
[&triangle_set, &triangles_area](
tbb::blocked_range<size_t> r) {
for (size_t t_idx = r.begin(); t_idx < r.end(); ++t_idx) {
const Vec3f &a = triangle_set.vertices[triangle_set.indices[t_idx].x()];
const Vec3f &b = triangle_set.vertices[triangle_set.indices[t_idx].y()];
const Vec3f &c = triangle_set.vertices[triangle_set.indices[t_idx].z()];
double area = double(0.5 * (b - a).cross(c - a).norm());
triangles_area[t_idx] = area;
}
});
std::map<double, size_t> area_sum_to_triangle_idx;
float area_sum = 0;
for (size_t t_idx = 0; t_idx < triangles_area.size(); ++t_idx) {
area_sum += triangles_area[t_idx];
area_sum_to_triangle_idx[area_sum] = t_idx;
}
std::mt19937_64 mersenne_engine { 27644437 };
// random numbers on interval [0, 1)
std::uniform_real_distribution<double> fdistribution;
auto get_random = [&fdistribution, &mersenne_engine]() {
return Vec3d { fdistribution(mersenne_engine), fdistribution(mersenne_engine), fdistribution(mersenne_engine) };
};
std::vector<Vec3d> random_samples(samples_count);
std::generate(random_samples.begin(), random_samples.end(), get_random);
TriangleSetSamples result;
result.total_area = area_sum;
result.positions.resize(samples_count);
result.normals.resize(samples_count);
result.triangle_indices.resize(samples_count);
tbb::parallel_for(tbb::blocked_range<size_t>(0, samples_count),
[&triangle_set, &area_sum_to_triangle_idx, &area_sum, &random_samples, &result](
tbb::blocked_range<size_t> r) {
for (size_t s_idx = r.begin(); s_idx < r.end(); ++s_idx) {
double t_sample = random_samples[s_idx].x() * area_sum;
size_t t_idx = area_sum_to_triangle_idx.upper_bound(t_sample)->second;
double sq_u = std::sqrt(random_samples[s_idx].y());
double v = random_samples[s_idx].z();
Vec3f A = triangle_set.vertices[triangle_set.indices[t_idx].x()];
Vec3f B = triangle_set.vertices[triangle_set.indices[t_idx].y()];
Vec3f C = triangle_set.vertices[triangle_set.indices[t_idx].z()];
result.positions[s_idx] = A * (1 - sq_u) + B * (sq_u * (1 - v)) + C * (v * sq_u);
result.normals[s_idx] = ((B - A).cross(C - B)).normalized();
result.triangle_indices[s_idx] = t_idx;
}
});
return result;
}
}

View file

@ -0,0 +1,20 @@
#ifndef SRC_LIBSLIC3R_TRIANGLESETSAMPLING_HPP_
#define SRC_LIBSLIC3R_TRIANGLESETSAMPLING_HPP_
#include <admesh/stl.h>
#include "libslic3r/Point.hpp"
namespace Slic3r {
struct TriangleSetSamples {
float total_area;
std::vector<Vec3f> positions;
std::vector<Vec3f> normals;
std::vector<size_t> triangle_indices;
};
TriangleSetSamples sample_its_uniform_parallel(size_t samples_count, const indexed_triangle_set &triangle_set);
}
#endif /* SRC_LIBSLIC3R_TRIANGLESETSAMPLING_HPP_ */

View file

@ -283,6 +283,7 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER
}
extern std::string xml_escape(std::string text, bool is_marked = false);
extern std::string xml_unescape(std::string text);
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__

View file

@ -24,6 +24,13 @@
#include <cmath>
#include <type_traits>
#ifdef _WIN32
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
// https://github.com/microsoft/STL/issues/147#issuecomment-1090148740
// Thus it is recommended to use boost::container::deque instead.
#include <boost/container/deque.hpp>
#endif // _WIN32
#include "Technologies.hpp"
#include "Semver.hpp"
@ -96,6 +103,16 @@ namespace Slic3r {
extern Semver SEMVER;
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
template<class T, class Allocator = std::allocator<T>>
using deque =
#ifdef _WIN32
// Use boost implementation, which allocates blocks of 512 bytes instead of blocks of 8 bytes.
boost::container::deque<T, Allocator>;
#else // _WIN32
std::deque<T, Allocator>;
#endif // _WIN32
template<typename T, typename Q>
inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); }
@ -341,6 +358,12 @@ public:
inline bool empty() const { return size() == 0; }
};
template<class T, class = FloatingOnly<T>>
constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
constexpr float NaNf = NaN<float>;
constexpr double NaNd = NaN<double>;
} // namespace Slic3r
#endif

View file

@ -1182,6 +1182,42 @@ std::string xml_escape(std::string text, bool is_marked/* = false*/)
return text;
}
std::string xml_unescape(std::string s)
{
std::string ret;
std::string::size_type i = 0;
std::string::size_type pos = 0;
while (i < s.size()) {
std::string rep;
if (s[i] == '&') {
if (s.substr(i, 4) == "&lt;") {
ret += s.substr(pos, i - pos) + "<";
i += 4;
pos = i;
}
else if (s.substr(i, 4) == "&gt;") {
ret += s.substr(pos, i - pos) + ">";
i += 4;
pos = i;
}
else if (s.substr(i, 5) == "&amp;") {
ret += s.substr(pos, i - pos) + "&";
i += 5;
pos = i;
}
else {
++i;
}
}
else {
++i;
}
}
ret += s.substr(pos);
return ret;
}
std::string format_memsize_MB(size_t n)
{
std::string out;