Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-09-01 13:28:10 +02:00
commit 639cf17e19
67 changed files with 1800 additions and 687 deletions

View file

@ -15,11 +15,6 @@
#include "Utils.hpp" // for next_highest_power_of_2()
extern "C"
{
// Ray-Triangle Intersection Test Routines by Tomas Moller, May 2000
#include <igl/raytri.c>
}
// Definition of the ray intersection hit structure.
#include <igl/Hit.h>
@ -231,6 +226,9 @@ namespace detail {
const VectorType origin;
const VectorType dir;
const VectorType invdir;
// epsilon for ray-triangle intersection, see intersect_triangle1()
const double eps;
};
template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType>
@ -283,44 +281,91 @@ namespace detail {
return tmin < t1 && tmax > t0;
}
// The following intersect_triangle() is derived from raytri.c routine intersect_triangle1()
// Ray-Triangle Intersection Test Routines
// Different optimizations of my and Ben Trumbore's
// code from journals of graphics tools (JGT)
// http://www.acm.org/jgt/
// by Tomas Moller, May 2000
template<typename V, typename W>
std::enable_if_t<std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()),
const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()),
&t, &u, &v);
std::enable_if_t<std::is_same<typename V::Scalar, double>::value&& std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &orig, const V &dir, const W &vert0, const W &vert1, const W &vert2, double &t, double &u, double &v, double eps)
{
// find vectors for two edges sharing vert0
const V edge1 = vert1 - vert0;
const V edge2 = vert2 - vert0;
// begin calculating determinant - also used to calculate U parameter
const V pvec = dir.cross(edge2);
// if determinant is near zero, ray lies in plane of triangle
const double det = edge1.dot(pvec);
V qvec;
if (det > eps) {
// calculate distance from vert0 to ray origin
V tvec = orig - vert0;
// calculate U parameter and test bounds
u = tvec.dot(pvec);
if (u < 0.0 || u > det)
return false;
// prepare to test V parameter
qvec = tvec.cross(edge1);
// calculate V parameter and test bounds
v = dir.dot(qvec);
if (v < 0.0 || u + v > det)
return false;
} else if (det < -eps) {
// calculate distance from vert0 to ray origin
V tvec = orig - vert0;
// calculate U parameter and test bounds
u = tvec.dot(pvec);
if (u > 0.0 || u < det)
return false;
// prepare to test V parameter
qvec = tvec.cross(edge1);
// calculate V parameter and test bounds
v = dir.dot(qvec);
if (v > 0.0 || u + v < det)
return false;
} else
// ray is parallel to the plane of the triangle
return false;
double inv_det = 1.0 / det;
// calculate t, ray intersects triangle
t = edge2.dot(qvec) * inv_det;
u *= inv_det;
v *= inv_det;
return true;
}
template<typename V, typename W>
std::enable_if_t<std::is_same<typename V::Scalar, double>::value && !std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector w0 = v0.template cast<double>();
Vector w1 = v1.template cast<double>();
Vector w2 = v2.template cast<double>();
return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()),
w0.data(), w1.data(), w2.data(), &t, &u, &v);
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) {
return intersect_triangle(origin, dir, v0.template cast<double>(), v1.template cast<double>(), v2.template cast<double>(), t, u, v, eps);
}
template<typename V, typename W>
std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector o = origin.template cast<double>();
Vector d = dir.template cast<double>();
return intersect_triangle1(o.data(), d.data(), const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()), &t, &u, &v);
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) {
return intersect_triangle(origin.template cast<double>(), dir.template cast<double>(), v0, v1, v2, t, u, v, eps);
}
template<typename V, typename W>
std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && ! std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector o = origin.template cast<double>();
Vector d = dir.template cast<double>();
Vector w0 = v0.template cast<double>();
Vector w1 = v1.template cast<double>();
Vector w2 = v2.template cast<double>();
return intersect_triangle1(o.data(), d.data(), w0.data(), w1.data(), w2.data(), &t, &u, &v);
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) {
return intersect_triangle(origin.template cast<double>(), dir.template cast<double>(), v0.template cast<double>(), v1.template cast<double>(), v2.template cast<double>(), t, u, v, eps);
}
template<typename Tree>
double intersect_triangle_epsilon(const Tree &tree) {
double eps = 0.000001;
if (! tree.empty()) {
const typename Tree::BoundingBox &bbox = tree.nodes().front().bbox;
double l = (bbox.max() - bbox.min()).cwiseMax();
if (l > 0)
eps /= (l * l);
}
return eps;
}
template<typename RayIntersectorType, typename Scalar>
@ -343,7 +388,7 @@ namespace detail {
if (intersect_triangle(
ray_intersector.origin, ray_intersector.dir,
ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],
t, u, v)
t, u, v, ray_intersector.eps)
&& t > 0.) {
hit = igl::Hit { int(node.idx), -1, float(u), float(v), float(t) };
return true;
@ -388,7 +433,7 @@ namespace detail {
if (intersect_triangle(
ray_intersector.origin, ray_intersector.dir,
ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],
t, u, v)
t, u, v, ray_intersector.eps)
&& t > 0.) {
ray_intersector.hits.emplace_back(igl::Hit{ int(node.idx), -1, float(u), float(v), float(t) });
}
@ -623,12 +668,15 @@ inline bool intersect_ray_first_hit(
// Direction of the ray.
const VectorType &dir,
// First intersection of the ray with the indexed triangle set.
igl::Hit &hit)
igl::Hit &hit,
// Epsilon for the ray-triangle intersection, it should be proportional to an average triangle edge length.
const double eps = 0.000001)
{
using Scalar = typename VectorType::Scalar;
auto ray_intersector = detail::RayIntersector<VertexType, IndexedFaceType, TreeType, VectorType> {
auto ray_intersector = detail::RayIntersector<VertexType, IndexedFaceType, TreeType, VectorType> {
vertices, faces, tree,
origin, dir, VectorType(dir.cwiseInverse())
origin, dir, VectorType(dir.cwiseInverse()),
eps
};
return ! tree.empty() && detail::intersect_ray_recursive_first_hit(
ray_intersector, size_t(0), std::numeric_limits<Scalar>::infinity(), hit);
@ -652,11 +700,14 @@ inline bool intersect_ray_all_hits(
// Direction of the ray.
const VectorType &dir,
// All intersections of the ray with the indexed triangle set, sorted by parameter t.
std::vector<igl::Hit> &hits)
std::vector<igl::Hit> &hits,
// Epsilon for the ray-triangle intersection, it should be proportional to an average triangle edge length.
const double eps = 0.000001)
{
auto ray_intersector = detail::RayIntersectorHits<VertexType, IndexedFaceType, TreeType, VectorType> {
{ vertices, faces, {tree},
origin, dir, VectorType(dir.cwiseInverse()) }
origin, dir, VectorType(dir.cwiseInverse()),
eps }
};
if (! tree.empty()) {
ray_intersector.hits.reserve(8);

View file

@ -134,10 +134,10 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
Polygons islands;
for (const PrintObject *object : top_level_objects_with_brim) {
//FIXME how about the brim type?
auto brim_offset = float(scale_(object->config().brim_offset.value));
auto brim_separation = float(scale_(object->config().brim_separation.value));
Polygons islands_object;
for (const ExPolygon &ex_poly : get_print_object_bottom_layer_expolygons(*object)) {
Polygons contour_offset = offset(ex_poly.contour, brim_offset);
Polygons contour_offset = offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare);
for (Polygon &poly : contour_offset)
poly.douglas_peucker(SCALED_RESOLUTION);
@ -166,7 +166,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
const BrimType brim_type = object->config().brim_type.value;
const float brim_offset = scale_(object->config().brim_offset.value);
const float brim_separation = scale_(object->config().brim_separation.value);
const float brim_width = scale_(object->config().brim_width.value);
const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
@ -174,16 +174,16 @@ static ExPolygons top_level_outer_brim_area(const Print &print
ExPolygons no_brim_area_object;
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim)
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_separation, ClipperLib::jtSquare), offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare)));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset, ClipperLib::jtSquare));
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly.holes));
if (brim_type != BrimType::btNoBrim)
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_offset));
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_separation, ClipperLib::jtSquare));
no_brim_area_object.emplace_back(ex_poly.contour);
}
@ -212,11 +212,11 @@ static ExPolygons inner_brim_area(const Print &print,
ExPolygons no_brim_area;
Polygons holes;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
const BrimType brim_type = object->config().brim_type.value;
const float brim_offset = scale_(object->config().brim_offset.value);
const float brim_width = scale_(object->config().brim_width.value);
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
const PrintObject *object = print.objects()[print_object_idx];
const BrimType brim_type = object->config().brim_type.value;
const float brim_separation = scale_(object->config().brim_separation.value);
const float brim_width = scale_(object->config().brim_width.value);
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
ExPolygons brim_area_object;
ExPolygons no_brim_area_object;
@ -226,21 +226,21 @@ static ExPolygons inner_brim_area(const Print &print,
if (top_outer_brim)
no_brim_area_object.emplace_back(ex_poly);
else
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_separation, ClipperLib::jtSquare), offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare)));
}
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_separation, ClipperLib::jtSquare), offset_ex(ex_poly.holes, -brim_width - brim_separation, ClipperLib::jtSquare)));
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly.holes));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset, ClipperLib::jtSquare));
append(holes_object, ex_poly.holes);
}
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_offset));
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_separation, ClipperLib::jtSquare));
for (const PrintInstance &instance : object->instances()) {
append_and_translate(brim_area, brim_area_object, instance);
@ -356,12 +356,12 @@ static void make_inner_brim(const Print &print,
Flow flow = print.brim_flow();
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
Polygons loops;
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare);
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare);
for (size_t i = 0; !islands_ex.empty(); ++i) {
for (ExPolygon &poly_ex : islands_ex)
poly_ex.douglas_peucker(SCALED_RESOLUTION);
polygons_append(loops, to_polygons(islands_ex));
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare);
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare);
}
loops = union_pt_chained_outside_in(loops);
@ -385,7 +385,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing()));
for (size_t i = 0; i < num_loops; ++i) {
try_cancel();
islands = offset(islands, float(flow.scaled_spacing()), jtSquare);
islands = offset(islands, float(flow.scaled_spacing()), ClipperLib::jtSquare);
for (Polygon &poly : islands)
poly.douglas_peucker(SCALED_RESOLUTION);
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));

View file

@ -696,10 +696,8 @@ ConfigSubstitutions ConfigBase::load_from_ini_string_commented(std::string &&dat
for (size_t i = 0; i < data.size();)
if (i == 0 || data[i] == '\n') {
// Start of a line.
if (i != 0) {
// Consume LF.
assert(data[i] == '\n');
// Don't keep empty lines.
if (data[i] == '\n') {
// Consume LF, don't keep empty lines.
if (j > 0 && data[j - 1] != '\n')
data[j ++] = data[i];
++ i;

View file

@ -49,6 +49,17 @@ const unsigned int VERSION_3MF = 1;
const unsigned int VERSION_3MF_COMPATIBLE = 2;
const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
// Painting gizmos data version numbers
// 0 : 3MF files saved by older PrusaSlicer or the painting gizmo wasn't used. No version definition in them.
// 1 : Introduction of painting gizmos data versioning. No other changes in painting gizmos data.
const unsigned int FDM_SUPPORTS_PAINTING_VERSION = 1;
const unsigned int SEAM_PAINTING_VERSION = 1;
const unsigned int MM_PAINTING_VERSION = 1;
const std::string SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION = "slic3rpe:FdmSupportsPaintingVersion";
const std::string SLIC3RPE_SEAM_PAINTING_VERSION = "slic3rpe:SeamPaintingVersion";
const std::string SLIC3RPE_MM_PAINTING_VERSION = "slic3rpe:MmPaintingVersion";
const std::string MODEL_FOLDER = "3D/";
const std::string MODEL_EXTENSION = ".model";
const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
@ -393,6 +404,10 @@ namespace Slic3r {
unsigned int m_version;
bool m_check_version;
unsigned int m_fdm_supports_painting_version = 0;
unsigned int m_seam_painting_version = 0;
unsigned int m_mm_painting_version = 0;
XML_Parser m_xml_parser;
// Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state
// after returning from XML_Parse() function, thus we keep the error state here.
@ -420,6 +435,7 @@ namespace Slic3r {
~_3MF_Importer();
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
unsigned int version() const { return m_version; }
private:
void _destroy_xml_parser();
@ -542,6 +558,9 @@ namespace Slic3r {
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version)
{
m_version = 0;
m_fdm_supports_painting_version = 0;
m_seam_painting_version = 0;
m_mm_painting_version = 0;
m_check_version = check_version;
m_model = &model;
m_unit_factor = 1.0f;
@ -1668,6 +1687,12 @@ namespace Slic3r {
return true;
}
inline static void check_painting_version(unsigned int loaded_version, unsigned int highest_supported_version, const std::string &error_msg)
{
if (loaded_version > highest_supported_version)
throw version_error(error_msg);
}
bool _3MF_Importer::_handle_end_metadata()
{
if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION) {
@ -1680,6 +1705,24 @@ namespace Slic3r {
}
}
if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
_(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")));
}
if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
_(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")));
}
if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
_(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")));
}
return true;
}
@ -1837,6 +1880,7 @@ namespace Slic3r {
}
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3;
unsigned int renamed_volumes_count = 0;
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
@ -1846,11 +1890,17 @@ namespace Slic3r {
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = false;
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// 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);
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
is_left_handed = Slic3r::Geometry::Transformation(volume_matrix_to_object).is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
break;
}
}
@ -1882,6 +1932,13 @@ namespace Slic3r {
stl_get_size(&stl);
triangle_mesh.repair();
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles
// This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative
if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets)
stl.stats.facets_reversed = 0;
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
@ -1945,6 +2002,14 @@ namespace Slic3r {
else
volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
}
// this may happen for 3mf saved by 3rd part softwares
if (volume->name.empty()) {
volume->name = object.name;
if (renamed_volumes_count > 0)
volume->name += "_" + std::to_string(renamed_volumes_count + 1);
++renamed_volumes_count;
}
}
return true;
@ -2271,6 +2336,16 @@ namespace Slic3r {
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
if (model.is_fdm_support_painted())
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION << "\">" << FDM_SUPPORTS_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
if (model.is_seam_painted())
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_SEAM_PAINTING_VERSION << "\">" << SEAM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
if (model.is_mm_painted())
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_MM_PAINTING_VERSION << "\">" << MM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
@ -2506,6 +2581,10 @@ namespace Slic3r {
if (volume == nullptr)
continue;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = volume->is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end());
@ -2520,6 +2599,15 @@ namespace Slic3r {
{
const Vec3i &idx = its.indices[i];
char *ptr = buf;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
"\" v3=\"" << boost::spirit::int_ << "\"",
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
#else
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
@ -2527,6 +2615,7 @@ namespace Slic3r {
idx[0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[2] + volume_it->second.first_vertex_id);
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
*ptr = '\0';
output_buffer += buf;
}
@ -2961,6 +3050,19 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
return true;
}
// Perform conversions based on the config values available.
//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
{
if (! config.has("brim_separation")) {
if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
// Conversion from older PrusaSlicer which applied brim separation equal to elephant foot compensation.
auto *opt_brim_separation = config.option<ConfigOptionFloat>("brim_separation", true);
opt_brim_separation->value = opt_elephant_foot->value;
}
}
}
bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version)
{
if (path == nullptr || model == nullptr)
@ -2971,6 +3073,7 @@ bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionCo
_3MF_Importer importer;
bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
importer.log_errors();
handle_legacy_project_loaded(importer.version(), config);
return res;
}

View file

@ -13,6 +13,7 @@
#include "ClipperUtils.hpp"
#include "libslic3r.h"
#include "LocalesUtils.hpp"
#include "libslic3r/format.hpp"
#include <algorithm>
#include <cstdlib>
@ -512,7 +513,8 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
// Check that there are extrusions on the very first layer.
// Check that there are extrusions on the very first layer. The case with empty
// first layer may result in skirt/brim in the air and maybe other issues.
if (layers_to_print.size() == 1u) {
if (!has_extrusions)
throw Slic3r::SlicingError(_(L("There is an object with no extrusions in the first layer.")) + "\n" +
@ -534,11 +536,12 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) {
const_cast<Print*>(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
_(L("Empty layers detected. Make sure the object is printable.")) + "\n" +
_(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " +
std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "
"usually caused by negligibly small extrusions or by a faulty model. Try to repair "
"the model or change its orientation on the bed.")));
Slic3r::format(_(L("Empty layer detected between heights %1% and %2%. Make sure the object is printable.")),
(last_extrusion_layer ? last_extrusion_layer->print_z() : 0.),
layers_to_print.back().print_z())
+ "\n" + Slic3r::format(_(L("Object name: %1%")), object.model_object()->name) + "\n\n"
+ _(L("This is usually caused by negligibly small extrusions or by a faulty model. "
"Try to repair the model or change its orientation on the bed.")));
}
// Remember last layer with extrusions.
@ -1974,6 +1977,7 @@ void GCode::process_layer(
}
gcode += this->change_layer(print_z); // this will increase m_layer_index
m_layer = &layer;
m_object_layer_over_raft = false;
if (! print.config().layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
@ -2232,8 +2236,13 @@ void GCode::process_layer(
gcode+="; PURGING FINISHED\n";
for (InstanceToPrint &instance_to_print : instances_to_print) {
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
// To control print speed of the 1st object layer printed over raft interface.
bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 &&
instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id();
m_config.apply(instance_to_print.print_object.config(), true);
m_layer = layers[instance_to_print.layer_id].layer();
m_layer = layer_to_print.layer();
m_object_layer_over_raft = object_layer_over_raft;
if (m_config.avoid_crossing_perimeters)
m_avoid_crossing_perimeters.init_layer(*m_layer);
if (this->config().gcode_label_objects)
@ -2246,11 +2255,13 @@ void GCode::process_layer(
m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset));
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
m_layer = layers[instance_to_print.layer_id].support_layer;
m_layer = layer_to_print.support_layer;
m_object_layer_over_raft = false;
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, 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));
m_layer = layers[instance_to_print.layer_id].layer();
m_layer = layer_to_print.layer();
m_object_layer_over_raft = object_layer_over_raft;
}
//FIXME order islands?
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
@ -2702,6 +2713,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double acceleration;
if (this->on_first_layer() && m_config.first_layer_acceleration.value > 0) {
acceleration = m_config.first_layer_acceleration.value;
} else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
acceleration = m_config.first_layer_acceleration_over_raft.value;
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
acceleration = m_config.perimeter_acceleration.value;
} else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) {
@ -2746,6 +2759,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
speed = m_volumetric_speed / path.mm3_per_mm;
if (this->on_first_layer())
speed = m_config.get_abs_value("first_layer_speed", speed);
else if (this->object_layer_over_raft())
speed = m_config.get_abs_value("first_layer_speed_over_raft", speed);
if (m_config.max_volumetric_speed.value > 0) {
// cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
speed = std::min(

View file

@ -125,7 +125,8 @@ public:
m_last_processor_extrusion_role(erNone),
m_layer_count(0),
m_layer_index(-1),
m_layer(nullptr),
m_layer(nullptr),
m_object_layer_over_raft(false),
m_volumetric_speed(0),
m_last_pos_defined(false),
m_last_extrusion_role(erNone),
@ -138,7 +139,7 @@ public:
m_silent_time_estimator_enabled(false),
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
{}
~GCode() {}
~GCode() = default;
// throws std::runtime_exception on error,
// throws CanceledException through print->throw_if_canceled().
@ -316,9 +317,11 @@ private:
unsigned int m_layer_count;
// Progress bar indicator. Increments from -1 up to layer_count.
int m_layer_index;
// Current layer processed. Insequential printing mode, only a single copy will be printed.
// Current layer processed. In sequential printing mode, only a single copy will be printed.
// In non-sequential mode, all its copies will be printed.
const Layer* m_layer;
// m_layer is an object layer and it is being printed over raft surface.
bool m_object_layer_over_raft;
double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active?
ExtrusionRole m_last_extrusion_role;
@ -373,6 +376,8 @@ private:
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
// On the first printing layer. This flag triggers first layer speeds.
bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }
// To control print speed of 1st object layer over raft interface.
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,

View file

@ -559,6 +559,21 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
}
bool Model::is_fdm_support_painted() const
{
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fdm_support_painted(); });
}
bool Model::is_seam_painted() const
{
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_seam_painted(); });
}
bool Model::is_mm_painted() const
{
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); });
}
ModelObject::~ModelObject()
{
this->clear_volumes();
@ -735,6 +750,16 @@ void ModelObject::clear_volumes()
this->invalidate_bounding_box();
}
bool ModelObject::is_fdm_support_painted() const
{
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_fdm_support_painted(); });
}
bool ModelObject::is_seam_painted() const
{
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_seam_painted(); });
}
bool ModelObject::is_mm_painted() const
{
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
@ -1200,9 +1225,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
for (ModelVolume *volume : volumes) {
const auto volume_matrix = volume->get_matrix();
volume->supported_facets.clear();
volume->seam_facets.clear();
volume->mmu_segmentation_facets.clear();
volume->supported_facets.reset();
volume->seam_facets.reset();
volume->mmu_segmentation_facets.reset();
if (! volume->is_model_part()) {
// Modifiers are not cut, but we still need to add the instance transformation
@ -2021,11 +2046,11 @@ bool FacetsAnnotation::set(const TriangleSelector& selector)
return false;
}
void FacetsAnnotation::clear()
void FacetsAnnotation::reset()
{
m_data.first.clear();
m_data.second.clear();
this->reset_timestamp();
this->touch();
}
// Following function takes data from a triangle and encodes it as string

View file

@ -285,6 +285,10 @@ public:
void clear_volumes();
void sort_volumes(bool full_sort);
bool is_multiparts() const { return volumes.size() > 1; }
// Checks if any of object volume is painted using the fdm support painting gizmo.
bool is_fdm_support_painted() const;
// Checks if any of object volume is painted using the seam painting gizmo.
bool is_seam_painted() const;
// Checks if any of object volume is painted using the multi-material painting gizmo.
bool is_mm_painted() const;
@ -541,7 +545,10 @@ public:
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
bool empty() const { return m_data.first.empty(); }
void clear();
// Following method clears the config and increases its timestamp, so the deleted
// state is considered changed from perspective of the undo/redo stack.
void reset();
// Serialize triangle into string, for serialization into 3MF/AMF.
std::string get_triangle_as_string(int i) const;
@ -720,6 +727,8 @@ public:
this->mmu_segmentation_facets.set_new_unique_id();
}
bool is_fdm_support_painted() const { return !this->supported_facets.empty(); }
bool is_seam_painted() const { return !this->seam_facets.empty(); }
bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); }
protected:
@ -1124,6 +1133,13 @@ public:
// Propose an output path, replace extension. The new_extension shall contain the initial dot.
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
// Checks if any of objects is painted using the fdm support painting gizmo.
bool is_fdm_support_painted() const;
// Checks if any of objects is painted using the seam painting gizmo.
bool is_seam_painted() const;
// Checks if any of objects is painted using the multi-material painting gizmo.
bool is_mm_painted() const;
private:
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }
void assign_new_unique_ids_recursive();

View file

@ -105,9 +105,6 @@ protected:
// The class tree will have virtual tables and type information.
virtual ~ObjectWithTimestamp() = default;
// Resetting timestamp to 1 indicates the object is in its initial (cleared) state.
// To be called by the derived class's clear() method.
void reset_timestamp() { m_timestamp = 1; }
// The timestamp uniquely identifies content of the derived class' data, therefore it makes sense to copy the timestamp if the content data was copied.
void copy_timestamp(const ObjectWithTimestamp& rhs) { m_timestamp = rhs.m_timestamp; }

View file

@ -428,9 +428,9 @@ static std::vector<std::string> s_Preset_print_options {
#endif /* HAS_PRESSURE_EQUALIZER */
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",

View file

@ -88,7 +88,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"filament_cost",
"filament_spool_weight",
"first_layer_acceleration",
"first_layer_acceleration_over_raft",
"first_layer_bed_temperature",
"first_layer_speed_over_raft",
"gcode_comments",
"gcode_label_objects",
"infill_acceleration",

View file

@ -505,10 +505,10 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<BrimType>(btOuterOnly));
def = this->add("brim_offset", coFloat);
def->label = L("Brim offset");
def = this->add("brim_separation", coFloat);
def->label = L("Brim separation gap");
def->category = L("Skirt and brim");
def->tooltip = L("The offset of the brim from the printed object. The offset is applied after the elephant foot compensation.");
def->tooltip = L("Offset of brim from the printed object. The offset is applied after the elephant foot compensation.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
@ -1152,6 +1152,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("first_layer_acceleration_over_raft", coFloat);
def->label = L("First object layer over raft interface");
def->tooltip = L("This is the acceleration your printer will use for first layer of object above raft interface. Set zero "
"to disable acceleration control for first layer of object above raft interface.");
def->sidetext = L("mm/s²");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("first_layer_bed_temperature", coInts);
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
@ -1194,6 +1203,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
def = this->add("first_layer_speed_over_raft", coFloatOrPercent);
def->label = L("Speed of object first layer over raft interface");
def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves "
"of the first object layer above raft interface, regardless of their type. If expressed as a percentage "
"(for example: 40%) it will scale the default speeds.");
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
def = this->add("first_layer_temperature", coInts);
def->label = L("First layer");
def->full_label = L("First layer nozzle temperature");

View file

@ -449,13 +449,15 @@ protected: \
PRINT_CONFIG_CLASS_DEFINE(
PrintObjectConfig,
((ConfigOptionFloat, brim_offset))
((ConfigOptionFloat, brim_separation))
((ConfigOptionEnum<BrimType>, brim_type))
((ConfigOptionFloat, brim_width))
((ConfigOptionBool, clip_multipart_objects))
((ConfigOptionBool, dont_support_bridges))
((ConfigOptionFloat, elefant_foot_compensation))
((ConfigOptionFloatOrPercent, extrusion_width))
((ConfigOptionFloat, first_layer_acceleration_over_raft))
((ConfigOptionFloatOrPercent, first_layer_speed_over_raft))
((ConfigOptionBool, infill_only_where_needed))
// Force the generation of solid shells between adjacent materials/volumes.
((ConfigOptionBool, interface_shells))
@ -1064,7 +1066,9 @@ Points get_bed_shape(const SLAPrinterConfig &cfg);
class ModelConfig
{
public:
void clear() { m_data.clear(); m_timestamp = 1; }
// Following method clears the config and increases its timestamp, so the deleted
// state is considered changed from perspective of the undo/redo stack.
void reset() { m_data.clear(); touch(); }
void assign_config(const ModelConfig &rhs) {
if (m_timestamp != rhs.m_timestamp) {
@ -1076,7 +1080,7 @@ public:
if (m_timestamp != rhs.m_timestamp) {
m_data = std::move(rhs.m_data);
m_timestamp = rhs.m_timestamp;
rhs.clear();
rhs.reset();
}
}

View file

@ -500,7 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(
bool invalidated = false;
for (const t_config_option_key &opt_key : opt_keys) {
if ( opt_key == "brim_width"
|| opt_key == "brim_offset"
|| opt_key == "brim_separation"
|| opt_key == "brim_type") {
// Brim is printed below supports, support invalidates brim and skirt.
steps.emplace_back(posSupportMaterial);
@ -2294,9 +2294,13 @@ void PrintObject::project_and_append_custom_facets(
? mv->seam_facets.get_facets_strict(*mv, type)
: mv->supported_facets.get_facets_strict(*mv, type);
if (! custom_facets.indices.empty())
#if 0
project_triangles_to_slabs(this->layers(), custom_facets,
(this->trafo_centered() * mv->get_matrix()).cast<float>(),
seam, out);
#else
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &out, [](){});
#endif
}
}

View file

@ -17,10 +17,18 @@ namespace sla {
class IndexedMesh::AABBImpl {
private:
AABBTreeIndirect::Tree3f m_tree;
double m_triangle_ray_epsilon;
public:
void init(const indexed_triangle_set &its)
void init(const indexed_triangle_set &its, bool calculate_epsilon)
{
m_triangle_ray_epsilon = 0.000001;
if (calculate_epsilon) {
// Calculate epsilon from average triangle edge length.
double l = its_average_edge_length(its);
if (l > 0)
m_triangle_ray_epsilon = 0.000001 * l * l;
}
m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
its.vertices, its.indices);
}
@ -31,7 +39,7 @@ public:
igl::Hit & hit)
{
AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices,
m_tree, s, dir, hit);
m_tree, s, dir, hit, m_triangle_ray_epsilon);
}
void intersect_ray(const indexed_triangle_set &its,
@ -40,7 +48,7 @@ public:
std::vector<igl::Hit> & hits)
{
AABBTreeIndirect::intersect_ray_all_hits(its.vertices, its.indices,
m_tree, s, dir, hits);
m_tree, s, dir, hits, m_triangle_ray_epsilon);
}
double squared_distance(const indexed_triangle_set & its,
@ -60,25 +68,25 @@ public:
}
};
template<class M> void IndexedMesh::init(const M &mesh)
template<class M> void IndexedMesh::init(const M &mesh, bool calculate_epsilon)
{
BoundingBoxf3 bb = bounding_box(mesh);
m_ground_level += bb.min(Z);
// Build the AABB accelaration tree
m_aabb->init(*m_tm);
m_aabb->init(*m_tm, calculate_epsilon);
}
IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh)
IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh, bool calculate_epsilon)
: m_aabb(new AABBImpl()), m_tm(&tmesh)
{
init(tmesh);
init(tmesh, calculate_epsilon);
}
IndexedMesh::IndexedMesh(const TriangleMesh &mesh)
IndexedMesh::IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon)
: m_aabb(new AABBImpl()), m_tm(&mesh.its)
{
init(mesh);
init(mesh, calculate_epsilon);
}
IndexedMesh::~IndexedMesh() {}

View file

@ -42,12 +42,14 @@ class IndexedMesh {
std::vector<DrainHole> m_holes;
#endif
template<class M> void init(const M &mesh);
template<class M> void init(const M &mesh, bool calculate_epsilon);
public:
explicit IndexedMesh(const indexed_triangle_set&);
explicit IndexedMesh(const TriangleMesh &mesh);
// calculate_epsilon ... calculate epsilon for triangle-ray intersection from an average triangle edge length.
// If set to false, a default epsilon is used, which works for "reasonable" meshes.
explicit IndexedMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon = false);
explicit IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon = false);
IndexedMesh(const IndexedMesh& other);
IndexedMesh& operator=(const IndexedMesh&);

View file

@ -37,6 +37,7 @@
#define DEBUG
#define _DEBUG
#undef NDEBUG
#include "utils.hpp"
#include "SVG.hpp"
#endif
@ -429,7 +430,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
for (const MyLayer *layer : top_contacts)
Slic3r::SVG::export_expolygons(
debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z),
union_ex(layer->polygons, false));
union_ex(layer->polygons));
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts";
@ -447,7 +448,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id)
Slic3r::SVG::export_expolygons(
debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z),
union_ex(layer_support_areas[layer_id], false));
union_ex(layer_support_areas[layer_id]));
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating intermediate layers - indices";
@ -466,7 +467,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
for (const MyLayer *layer : top_contacts)
Slic3r::SVG::export_expolygons(
debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z),
union_ex(layer->polygons, false));
union_ex(layer->polygons));
#endif
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
@ -478,7 +479,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it)
Slic3r::SVG::export_expolygons(
debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z),
union_ex((*it)->polygons, false));
union_ex((*it)->polygons));
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts";
@ -507,11 +508,11 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
for (const MyLayer *l : interface_layers)
Slic3r::SVG::export_expolygons(
debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z),
union_ex(l->polygons, false));
union_ex(l->polygons));
for (const MyLayer *l : base_interface_layers)
Slic3r::SVG::export_expolygons(
debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z),
union_ex(l->polygons, false));
union_ex(l->polygons));
#endif // SLIC3R_DEBUG
/*
@ -1308,9 +1309,9 @@ namespace SupportMaterialInternal {
#ifdef SLIC3R_DEBUG
static int iRun = 0;
SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++),
{ { { union_ex(offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), false) }, { "unsupported_bridge_edges", "orange", 0.5f } },
{ { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(bridges, false) }, { "bridges", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } },
{ { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(bridges) }, { "bridges", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
}
}
@ -1416,13 +1417,35 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
// Generate overhang / contact_polygons for non-raft layers.
const Layer &lower_layer = *layer.lower_layer;
const bool has_enforcer = ! annotations.enforcers_layers.empty() && ! annotations.enforcers_layers[layer_id].empty();
float fw = 0;
// Cache support trimming polygons derived from lower layer polygons, possible merged with "on build plate only" trimming polygons.
auto slices_margin_update =
[&slices_margin, &lower_layer, &lower_layer_polygons, buildplate_only, has_enforcer, &annotations, layer_id]
(float slices_margin_offset, float no_interface_offset) {
if (slices_margin.offset != slices_margin_offset) {
slices_margin.offset = slices_margin_offset;
slices_margin.polygons = (slices_margin_offset == 0.f) ?
lower_layer_polygons :
offset2(lower_layer.lslices, -no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (buildplate_only && !annotations.buildplate_covered[layer_id].empty()) {
if (has_enforcer)
// Make a backup of trimming polygons before enforcing "on build plate only".
slices_margin.all_polygons = slices_margin.polygons;
// Trim the inflated contact surfaces by the top surfaces as well.
slices_margin.polygons = union_(slices_margin.polygons, annotations.buildplate_covered[layer_id]);
}
}
};
float fw = 0;
float lower_layer_offset = 0;
float no_interface_offset = 0;
for (LayerRegion *layerm : layer.regions()) {
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
fw = float(layerm->flow(frExternalPerimeter).scaled_width());
no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
lower_layer_offset =
(layer_id < (size_t)object_config.support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
0.f :
@ -1494,7 +1517,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
iRun, layer_id,
std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()),
get_extents(diff_polygons));
Slic3r::ExPolygons expolys = union_ex(diff_polygons, false);
Slic3r::ExPolygons expolys = union_ex(diff_polygons);
svg.draw(expolys);
}
#endif /* SLIC3R_DEBUG */
@ -1512,7 +1535,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
iRun, layer_id,
std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(),
layer.print_z),
union_ex(diff_polygons, false));
union_ex(diff_polygons));
#endif /* SLIC3R_DEBUG */
//FIXME the overhang_polygons are used to construct the support towers as well.
@ -1529,20 +1552,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
//FIXME one should trim with the layer span colliding with the support layer, this layer
// may be lower than lower_layer, so the support area needed may need to be actually bigger!
// For the same reason, the non-bridging support area may be smaller than the bridging support area!
float slices_margin_offset = std::min(lower_layer_offset, float(scale_(gap_xy)));
if (slices_margin.offset != slices_margin_offset) {
slices_margin.offset = slices_margin_offset;
slices_margin.polygons = (slices_margin_offset == 0.f) ?
lower_layer_polygons :
offset2(lower_layer.lslices, - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) {
if (has_enforcer)
// Make a backup of trimming polygons before enforcing "on build plate only".
slices_margin.all_polygons = slices_margin.polygons;
// Trim the inflated contact surfaces by the top surfaces as well.
slices_margin.polygons = union_(slices_margin.polygons, annotations.buildplate_covered[layer_id]);
}
}
slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset);
// Offset the contact polygons outside.
#if 0
for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) {
@ -1572,12 +1582,13 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { layer.lslices, { "layer.lslices", "gray", 0.2f } },
{ { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "green", 0.5f } },
{ enforcers_united, { "enforcers", "blue", 0.5f } },
{ { union_ex(enforcer_polygons, true) }, { "new_contacts", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { layer.lslices, { "layer.lslices", "gray", 0.2f } },
{ { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "green", 0.5f } },
{ enforcers_united, { "enforcers", "blue", 0.5f } },
{ { union_safety_offset_ex(enforcer_polygons) }, { "new_contacts", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
polygons_append(overhang_polygons, enforcer_polygons);
slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset);
polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons));
}
}
@ -1738,19 +1749,19 @@ static inline void fill_contact_layer(
#endif // SLIC3R_DEBUG
);
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
{ { union_ex(slices_margin.polygons, false) }, { "slices_margin_cached", "blue", 0.5f } },
{ { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } },
{ { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
SVG::export_expolygons(debug_out_path("support-top-contacts-final1-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
{ { union_ex(slices_margin.polygons) }, { "slices_margin_cached", "blue", 0.5f } },
{ { union_ex(dense_interface_polygons) }, { "dense_interface_polygons", "green", 0.5f } },
{ { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
//support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z));
SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
{ { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } },
{ { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
SVG::export_expolygons(debug_out_path("support-top-contacts-final2-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
{ { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(dense_interface_polygons) }, { "dense_interface_polygons", "green", 0.5f } },
{ { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
}
}
@ -1796,11 +1807,11 @@ static inline void fill_contact_layer(
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
{ { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(overhang_polygons, false) }, { "overhang_polygons", "green", 0.5f } },
{ { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
{ { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(overhang_polygons) }, { "overhang_polygons", "green", 0.5f } },
{ { union_safety_offset_ex(new_layer.polygons) }, { "new_layer.polygons", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
@ -1964,10 +1975,10 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
Polygons top = collect_region_slices_by_type(layer, stTop);
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z),
{ { { union_ex(top, false) }, { "top", "blue", 0.5f } },
{ { union_ex(supports_projected, true) }, { "overhangs", "magenta", 0.5f } },
{ layer.lslices, { "layer.lslices", "green", 0.5f } },
{ { union_ex(polygons_new, true) }, { "polygons_new", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(top) }, { "top", "blue", 0.5f } },
{ { union_safety_offset_ex(supports_projected) }, { "overhangs", "magenta", 0.5f } },
{ layer.lslices, { "layer.lslices", "green", 0.5f } },
{ { union_safety_offset_ex(polygons_new) }, { "polygons_new", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
// Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any
@ -2037,7 +2048,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z),
union_ex(layer_new.polygons, false));
union_ex(layer_new.polygons));
#endif /* SLIC3R_DEBUG */
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
@ -2050,14 +2061,14 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
if (! layer_support_areas[layer_id_above].empty()) {
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z),
{ { { union_ex(touching, false) }, { "touching", "blue", 0.5f } },
{ { union_ex(layer_support_areas[layer_id_above], true) }, { "above", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(touching) }, { "touching", "blue", 0.5f } },
{ { union_safety_offset_ex(layer_support_areas[layer_id_above]) }, { "above", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z),
union_ex(layer_support_areas[layer_id_above], false));
union_ex(layer_support_areas[layer_id_above]));
#endif /* SLIC3R_DEBUG */
}
}
@ -2080,8 +2091,8 @@ static inline std::pair<Polygons, Polygons> project_support_to_grid(const Layer
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-support-areas-%s-raw-%d-%lf.svg", debug_name, iRun, layer.print_z),
{ { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } },
{ { union_ex(overhangs_projection, true) }, { "overhangs_projection", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(trimming) }, { "trimming", "blue", 0.5f } },
{ { union_safety_offset_ex(overhangs_projection) }, { "overhangs_projection", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
remove_sticks(overhangs_projection);
@ -2089,8 +2100,8 @@ static inline std::pair<Polygons, Polygons> project_support_to_grid(const Layer
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-support-areas-%s-raw-cleaned-%d-%lf.svg", debug_name, iRun, layer.print_z),
{ { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } },
{ { union_ex(overhangs_projection, false) }, { "overhangs_projection", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(trimming) }, { "trimming", "blue", 0.5f } },
{ { union_ex(overhangs_projection) }, { "overhangs_projection", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
SupportGridPattern support_grid_pattern(&overhangs_projection, &trimming, grid_params);
@ -2113,7 +2124,7 @@ static inline std::pair<Polygons, Polygons> project_support_to_grid(const Layer
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-layer_support_area-gridded-%s-%d-%lf.svg", debug_name, iRun, layer.print_z),
union_ex(out.first, false));
union_ex(out.first));
#endif /* SLIC3R_DEBUG */
});
@ -2131,13 +2142,13 @@ static inline std::pair<Polygons, Polygons> project_support_to_grid(const Layer
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
union_ex(out.second, false));
union_ex(out.second));
#endif /* SLIC3R_DEBUG */
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
{ { { union_ex(trimming, false) }, { "trimming", "gray", 0.5f } },
{ { union_ex(overhangs_projection, true) }, { "overhangs_projection", "blue", 0.5f } },
{ { union_ex(out.second, true) }, { "projection_new", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
{ { { union_ex(trimming) }, { "trimming", "gray", 0.5f } },
{ { union_safety_offset_ex(overhangs_projection) }, { "overhangs_projection", "blue", 0.5f } },
{ { union_safety_offset_ex(out.second) }, { "projection_new", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
});
@ -2667,10 +2678,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
BoundingBox bbox = get_extents(polygons_new);
bbox.merge(get_extents(polygons_trimming));
::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-raw-%d-%lf.svg", iRun, layer_intermediate.print_z), bbox);
svg.draw(union_ex(polygons_new, false), "blue", 0.5f);
svg.draw(to_polylines(polygons_new), "blue");
svg.draw(union_ex(polygons_trimming, true), "red", 0.5f);
svg.draw(to_polylines(polygons_trimming), "red");
svg.draw(union_ex(polygons_new), "blue", 0.5f);
svg.draw(to_polylines(polygons_new), "blue");
svg.draw(union_safety_offset_ex(polygons_trimming), "red", 0.5f);
svg.draw(to_polylines(polygons_trimming), "red");
}
#endif /* SLIC3R_DEBUG */
@ -2706,7 +2717,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it)
::Slic3r::SVG::export_expolygons(
debug_out_path("support-intermediate-layers-untrimmed-%d-%lf.svg", iRun, (*it)->print_z),
union_ex((*it)->polygons, false));
union_ex((*it)->polygons));
++ iRun;
#endif /* SLIC3R_DEBUG */
@ -2799,22 +2810,22 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
Polygons brim;
if (object.has_brim()) {
// Calculate the area covered by the brim.
const BrimType brim_type = object.config().brim_type;
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
const auto brim_offset = scaled<float>(object.config().brim_offset.value + object.config().brim_width.value);
const BrimType brim_type = object.config().brim_type;
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
const auto brim_separation = scaled<float>(object.config().brim_separation.value + object.config().brim_width.value);
for (const ExPolygon &ex : object.layers().front()->lslices) {
if (brim_outer && brim_inner)
polygons_append(brim, offset(ex, brim_offset));
polygons_append(brim, offset(ex, brim_separation));
else {
if (brim_outer)
polygons_append(brim, offset(ex.contour, brim_offset, ClipperLib::jtRound, float(scale_(0.1))));
polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1))));
else
brim.emplace_back(ex.contour);
if (brim_inner) {
Polygons holes = ex.holes;
polygons_reverse(holes);
holes = offset(holes, - brim_offset, ClipperLib::jtRound, float(scale_(0.1)));
holes = offset(holes, - brim_separation, ClipperLib::jtRound, float(scale_(0.1)));
polygons_reverse(holes);
polygons_append(brim, std::move(holes));
} else

View file

@ -49,6 +49,10 @@
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0)
// Enable implementation of retract acceleration in gcode processor
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0)
// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA0)
// Enable rendering seams (and other options) in preview using models
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA0)
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
#define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA0)

View file

@ -1275,6 +1275,21 @@ float its_volume(const indexed_triangle_set &its)
return volume;
}
float its_average_edge_length(const indexed_triangle_set &its)
{
if (its.indices.empty())
return 0.f;
double edge_length = 0.f;
for (size_t i = 0; i < its.indices.size(); ++ i) {
const its_triangle v = its_triangle_vertices(its, i);
edge_length += (v[1] - v[0]).cast<double>().norm() +
(v[2] - v[0]).cast<double>().norm() +
(v[1] - v[2]).cast<double>().norm();
}
return float(edge_length / (3 * its.indices.size()));
}
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its)
{
return its_split<>(its);

View file

@ -199,6 +199,7 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its,
}
float its_volume(const indexed_triangle_set &its);
float its_average_edge_length(const indexed_triangle_set &its);
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);

View file

@ -58,6 +58,11 @@ void set_data_dir(const std::string &path);
// Return a full path to the GUI resource files.
const std::string& data_dir();
// Format an output path for debugging purposes.
// Writes out the output path prefix to the console for the first time the function is called,
// so the user knows where to search for the debugging output.
std::string debug_out_path(const char *name, ...);
// A special type for strings encoded in the local Windows 8-bit code page.
// This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded.
typedef std::string local_encoded_string;

View file

@ -65,18 +65,6 @@ static constexpr double EXTERNAL_INFILL_MARGIN = 3.;
#define SCALED_EPSILON scale_(EPSILON)
#define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/"
inline std::string debug_out_path(const char *name, ...)
{
char buffer[2048];
va_list args;
va_start(args, name);
std::vsprintf(buffer, name, args);
va_end(args);
return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer);
}
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif /* UNUSED */

View file

@ -1,6 +1,7 @@
#include "Utils.hpp"
#include "I18N.hpp"
#include <atomic>
#include <locale>
#include <ctime>
#include <cstdarg>
@ -207,6 +208,23 @@ std::string custom_shapes_dir()
return (boost::filesystem::path(g_data_dir) / "shapes").string();
}
static std::atomic<bool> debug_out_path_called(false);
std::string debug_out_path(const char *name, ...)
{
static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/";
if (! debug_out_path_called.exchange(true)) {
std::string path = boost::filesystem::system_complete(SLIC3R_DEBUG_OUT_PATH_PREFIX).string();
printf("Debugging output files will be written to %s\n", path.c_str());
}
char buffer[2048];
va_list args;
va_start(args, name);
std::vsprintf(buffer, name, args);
va_end(args);
return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer);
}
#ifdef _WIN32
// The following helpers are borrowed from the LLVM project https://github.com/llvm
namespace WindowsSupport