mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Fixed conflicts after merge with master
This commit is contained in:
commit
639cf17e19
67 changed files with 1800 additions and 687 deletions
|
@ -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);
|
||||
|
|
|
@ -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())));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -119,7 +119,7 @@ void Bed3D::Axes::render() const
|
|||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.0);
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
|
||||
// x axis
|
||||
const_cast<GLModel*>(&m_arrow)->set_color(-1, { 0.75f, 0.0f, 0.0f, 1.0f });
|
||||
|
@ -498,7 +498,7 @@ void Bed3D::render_model() const
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.0);
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()));
|
||||
model->render();
|
||||
|
|
|
@ -281,7 +281,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
toggle_field(el, have_skirt);
|
||||
|
||||
bool have_brim = config->opt_enum<BrimType>("brim_type") != btNoBrim;
|
||||
for (auto el : { "brim_width", "brim_offset" })
|
||||
for (auto el : { "brim_width", "brim_separation" })
|
||||
toggle_field(el, have_brim);
|
||||
// perimeter_extruder uses the same logic as in Print::extruders()
|
||||
toggle_field("perimeter_extruder", have_perimeters || have_brim);
|
||||
|
@ -312,7 +312,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
toggle_field("support_material_speed", have_support_material || have_brim || have_skirt);
|
||||
|
||||
toggle_field("raft_contact_distance", have_raft && !have_support_soluble);
|
||||
toggle_field("raft_expansion", have_raft);
|
||||
for (auto el : { "raft_expansion", "first_layer_acceleration_over_raft", "first_layer_speed_over_raft" })
|
||||
toggle_field(el, have_raft);
|
||||
|
||||
bool has_ironing = config->opt_bool("ironing");
|
||||
for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_speed" })
|
||||
|
|
|
@ -22,6 +22,95 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
namespace {
|
||||
|
||||
// escaping of path string according to
|
||||
// https://cgit.freedesktop.org/xdg/xdg-specs/tree/desktop-entry/desktop-entry-spec.xml
|
||||
std::string escape_string(const std::string& str)
|
||||
{
|
||||
// The buffer needs to be bigger if escaping <,>,&
|
||||
std::vector<char> out(str.size() * 4, 0);
|
||||
char *outptr = out.data();
|
||||
for (size_t i = 0; i < str.size(); ++ i) {
|
||||
char c = str[i];
|
||||
// must be escaped
|
||||
if (c == '\"') { //double quote
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\"';
|
||||
} else if (c == '`') { // backtick character
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '`';
|
||||
} else if (c == '$') { // dollar sign
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '$';
|
||||
} else if (c == '\\') { // backslash character
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
// Reserved characters
|
||||
// At Ubuntu, all these characters must NOT be escaped for desktop integration to work
|
||||
/*
|
||||
} else if (c == ' ') { // space
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = ' ';
|
||||
} else if (c == '\t') { // tab
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\t';
|
||||
} else if (c == '\n') { // newline
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\n';
|
||||
} else if (c == '\'') { // single quote
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\'';
|
||||
} else if (c == '>') { // greater-than sign
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '&';
|
||||
(*outptr ++) = 'g';
|
||||
(*outptr ++) = 't';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == '<') { //less-than sign
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '&';
|
||||
(*outptr ++) = 'l';
|
||||
(*outptr ++) = 't';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == '~') { // tilde
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '~';
|
||||
} else if (c == '|') { // vertical bar
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '|';
|
||||
} else if (c == '&') { // ampersand
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '&';
|
||||
(*outptr ++) = 'a';
|
||||
(*outptr ++) = 'm';
|
||||
(*outptr ++) = 'p';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == ';') { // semicolon
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = ';';
|
||||
} else if (c == '*') { //asterisk
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '*';
|
||||
} else if (c == '?') { // question mark
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '?';
|
||||
} else if (c == '#') { // hash mark
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '#';
|
||||
} else if (c == '(') { // parenthesis
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '(';
|
||||
} else if (c == ')') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = ')';
|
||||
*/
|
||||
} else
|
||||
(*outptr ++) = c;
|
||||
}
|
||||
return std::string(out.data(), outptr - out.data());
|
||||
}
|
||||
// Disects path strings stored in system variable divided by ':' and adds into vector
|
||||
void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
|
||||
{
|
||||
|
@ -157,7 +246,8 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
}
|
||||
|
||||
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path'
|
||||
boost::replace_all(excutable_path, "'", "'\\''");
|
||||
//boost::replace_all(excutable_path, "'", "'\\''");
|
||||
excutable_path = escape_string(excutable_path);
|
||||
|
||||
// Find directories icons and applications
|
||||
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
||||
|
@ -243,14 +333,14 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
"Name=PrusaSlicer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer%2%\n"
|
||||
"Exec=\'%3%\' %%F\n"
|
||||
"Exec=\"%3%\" %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
|
||||
"Categories=Graphics;3DGraphics;Engineering;\n"
|
||||
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
|
||||
"StartupNotify=false\n"
|
||||
"StartupWMClass=prusa-slicer", name_suffix, version_suffix, excutable_path);
|
||||
"StartupWMClass=prusa-slicer\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(path, desktop_file)){
|
||||
|
@ -292,40 +382,44 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
app_config->set("desktop_integration_app_path", GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix));
|
||||
|
||||
// Repeat for Gcode viewer - use same paths as for slicer files
|
||||
// Icon
|
||||
if (!target_dir_icons.empty())
|
||||
{
|
||||
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (copy_icon(icon_path, dest_path))
|
||||
// save path to icon
|
||||
app_config->set("desktop_integration_icon_viewer_path", dest_path);
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
|
||||
}
|
||||
// Do NOT add gcode viewer desktop file on ChromeOS
|
||||
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
|
||||
// Icon
|
||||
if (!target_dir_icons.empty())
|
||||
{
|
||||
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (copy_icon(icon_path, dest_path))
|
||||
// save path to icon
|
||||
app_config->set("desktop_integration_icon_viewer_path", dest_path);
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
|
||||
}
|
||||
|
||||
// Desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Prusa Gcode Viewer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer-gcodeviewer%2%\n"
|
||||
"Exec=\'%3%\' --gcodeviwer %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/x.gcode;\n"
|
||||
"Categories=Graphics;3DGraphics;\n"
|
||||
"Keywords=3D;Printing;Slicer;\n"
|
||||
"StartupNotify=false", name_suffix, version_suffix, excutable_path);
|
||||
// Desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Prusa Gcode Viewer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer-gcodeviewer%2%\n"
|
||||
"Exec=\"%3%\" --gcodeviewer %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/x.gcode;\n"
|
||||
"Categories=Graphics;3DGraphics;\n"
|
||||
"Keywords=3D;Printing;Slicer;\n"
|
||||
"StartupNotify=false\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file))
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file))
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||
|
@ -343,17 +437,20 @@ void DesktopIntegrationDialog::undo_desktop_intgration()
|
|||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viwer .desktop
|
||||
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viewer icon
|
||||
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
// No gcode viewer at ChromeOS
|
||||
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
|
||||
// gcode viewer .desktop
|
||||
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viewer icon
|
||||
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
}
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -22,17 +22,22 @@ namespace GUI {
|
|||
class GCodeViewer
|
||||
{
|
||||
using IBufferType = unsigned short;
|
||||
using Color = std::array<float, 3>;
|
||||
using Color = std::array<float, 4>;
|
||||
using VertexBuffer = std::vector<float>;
|
||||
using MultiVertexBuffer = std::vector<VertexBuffer>;
|
||||
using IndexBuffer = std::vector<IBufferType>;
|
||||
using MultiIndexBuffer = std::vector<IndexBuffer>;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
using InstanceBuffer = std::vector<float>;
|
||||
using InstanceIdBuffer = std::vector<size_t>;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
static const std::vector<Color> Extrusion_Role_Colors;
|
||||
static const std::vector<Color> Options_Colors;
|
||||
static const std::vector<Color> Travel_Colors;
|
||||
static const Color Wipe_Color;
|
||||
static const std::vector<Color> Range_Colors;
|
||||
static const Color Wipe_Color;
|
||||
static const Color Neutral_Color;
|
||||
|
||||
enum class EOptionsColors : unsigned char
|
||||
{
|
||||
|
@ -80,7 +85,10 @@ class GCodeViewer
|
|||
size_t position_size_floats() const { return 3; }
|
||||
size_t position_size_bytes() const { return position_size_floats() * sizeof(float); }
|
||||
|
||||
size_t normal_offset_floats() const { return position_size_floats(); }
|
||||
size_t normal_offset_floats() const {
|
||||
assert(format == EFormat::PositionNormal1 || format == EFormat::PositionNormal3);
|
||||
return position_size_floats();
|
||||
}
|
||||
size_t normal_offset_bytes() const { return normal_offset_floats() * sizeof(float); }
|
||||
|
||||
size_t normal_size_floats() const {
|
||||
|
@ -96,6 +104,47 @@ class GCodeViewer
|
|||
void reset();
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
// buffer containing instances data used to render a toolpaths using instanced models
|
||||
// instance record format: 5 floats -> position.x|position.y|position.z|width|height
|
||||
// which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced()
|
||||
struct InstanceVBuffer
|
||||
{
|
||||
// ranges used to render only subparts of the intances
|
||||
struct Ranges
|
||||
{
|
||||
struct Range
|
||||
{
|
||||
// offset in bytes of the 1st instance to render
|
||||
unsigned int offset;
|
||||
// count of instances to render
|
||||
unsigned int count;
|
||||
// vbo id
|
||||
unsigned int vbo{ 0 };
|
||||
// Color to apply to the instances
|
||||
Color color;
|
||||
};
|
||||
|
||||
std::vector<Range> ranges;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// cpu-side buffer containing all instances data
|
||||
InstanceBuffer buffer;
|
||||
// indices of the moves for all instances
|
||||
std::vector<size_t> s_ids;
|
||||
Ranges render_ranges;
|
||||
|
||||
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
|
||||
|
||||
size_t instance_size_floats() const { return 5; }
|
||||
size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); }
|
||||
|
||||
void reset();
|
||||
};
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
|
||||
struct IBuffer
|
||||
{
|
||||
|
@ -229,13 +278,34 @@ class GCodeViewer
|
|||
{
|
||||
Point,
|
||||
Line,
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
Triangle,
|
||||
Model
|
||||
#else
|
||||
Triangle
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
};
|
||||
|
||||
ERenderPrimitiveType render_primitive_type;
|
||||
|
||||
// buffers for point, line and triangle primitive types
|
||||
VBuffer vertices;
|
||||
std::vector<IBuffer> indices;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
struct Model
|
||||
{
|
||||
GLModel model;
|
||||
Color color;
|
||||
InstanceVBuffer instances;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// contain the buffer for model primitive types
|
||||
Model model;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
std::string shader;
|
||||
std::vector<Path> paths;
|
||||
// std::set seems to perform significantly better, at least on Windows.
|
||||
|
@ -283,9 +353,24 @@ class GCodeViewer
|
|||
}
|
||||
size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
bool has_data() const {
|
||||
switch (render_primitive_type)
|
||||
{
|
||||
case ERenderPrimitiveType::Point:
|
||||
case ERenderPrimitiveType::Line:
|
||||
case ERenderPrimitiveType::Triangle: {
|
||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
case ERenderPrimitiveType::Model: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool has_data() const {
|
||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
};
|
||||
|
||||
// helper to render shells
|
||||
|
@ -433,18 +518,30 @@ class GCodeViewer
|
|||
int64_t gl_multi_lines_calls_count{ 0 };
|
||||
int64_t gl_multi_triangles_calls_count{ 0 };
|
||||
int64_t gl_triangles_calls_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t gl_instanced_models_calls_count{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
// memory
|
||||
int64_t results_size{ 0 };
|
||||
int64_t total_vertices_gpu_size{ 0 };
|
||||
int64_t total_indices_gpu_size{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t total_instances_gpu_size{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
int64_t max_vbuffer_gpu_size{ 0 };
|
||||
int64_t max_ibuffer_gpu_size{ 0 };
|
||||
int64_t paths_size{ 0 };
|
||||
int64_t render_paths_size{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t models_instances_size{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
// other
|
||||
int64_t travel_segments_count{ 0 };
|
||||
int64_t wipe_segments_count{ 0 };
|
||||
int64_t extrude_segments_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t instances_count{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
int64_t vbuffers_count{ 0 };
|
||||
int64_t ibuffers_count{ 0 };
|
||||
|
||||
|
@ -470,22 +567,34 @@ class GCodeViewer
|
|||
gl_multi_lines_calls_count = 0;
|
||||
gl_multi_triangles_calls_count = 0;
|
||||
gl_triangles_calls_count = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
gl_instanced_models_calls_count = 0;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
void reset_sizes() {
|
||||
results_size = 0;
|
||||
total_vertices_gpu_size = 0;
|
||||
total_indices_gpu_size = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
total_instances_gpu_size = 0;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
max_vbuffer_gpu_size = 0;
|
||||
max_ibuffer_gpu_size = 0;
|
||||
paths_size = 0;
|
||||
render_paths_size = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
models_instances_size = 0;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
void reset_others() {
|
||||
travel_segments_count = 0;
|
||||
wipe_segments_count = 0;
|
||||
extrude_segments_count = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
instances_count = 0;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
vbuffers_count = 0;
|
||||
ibuffers_count = 0;
|
||||
}
|
||||
|
@ -563,6 +672,9 @@ public:
|
|||
Endpoints endpoints;
|
||||
Endpoints current;
|
||||
Endpoints last_current;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
Endpoints global;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
Vec3f current_position{ Vec3f::Zero() };
|
||||
Marker marker;
|
||||
GCodeWindow gcode_window;
|
||||
|
@ -632,7 +744,7 @@ public:
|
|||
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
|
||||
|
||||
void reset();
|
||||
void render() const;
|
||||
void render();
|
||||
|
||||
bool has_data() const { return !m_roles.empty(); }
|
||||
bool can_export_toolpaths() const;
|
||||
|
@ -678,17 +790,18 @@ private:
|
|||
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
|
||||
void load_shells(const Print& print, bool initialized);
|
||||
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
|
||||
void render_toolpaths() const;
|
||||
void render_shells() const;
|
||||
void render_legend(float& legend_height) const;
|
||||
void render_toolpaths();
|
||||
void render_shells();
|
||||
void render_legend(float& legend_height);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void render_statistics() const;
|
||||
void render_statistics();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
bool is_visible(ExtrusionRole role) const {
|
||||
return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0;
|
||||
}
|
||||
bool is_visible(const Path& path) const { return is_visible(path.role); }
|
||||
void log_memory_used(const std::string& label, int64_t additional = 0) const;
|
||||
Color option_color(EMoveType move_type) const;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -4159,7 +4159,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.0);
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
|
||||
for (GLVolume* vol : visible_volumes) {
|
||||
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray);
|
||||
|
@ -5181,7 +5181,7 @@ void GLCanvas3D::_render_objects()
|
|||
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_gcode() const
|
||||
void GLCanvas3D::_render_gcode()
|
||||
{
|
||||
m_gcode_viewer.render();
|
||||
}
|
||||
|
|
|
@ -904,7 +904,7 @@ private:
|
|||
#else
|
||||
void _render_objects();
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void _render_gcode() const;
|
||||
void _render_gcode();
|
||||
void _render_selection() const;
|
||||
void _render_sequential_clearance();
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
|
|
|
@ -208,6 +208,85 @@ void GLModel::render() const
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const
|
||||
{
|
||||
if (instances_vbo == 0)
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_current_shader();
|
||||
assert(shader == nullptr || boost::algorithm::iends_with(shader->get_name(), "_instanced"));
|
||||
|
||||
// vertex attributes
|
||||
GLint position_id = (shader != nullptr) ? shader->get_attrib_location("v_position") : -1;
|
||||
GLint normal_id = (shader != nullptr) ? shader->get_attrib_location("v_normal") : -1;
|
||||
assert(position_id != -1 && normal_id != -1);
|
||||
|
||||
// instance attributes
|
||||
GLint offset_id = (shader != nullptr) ? shader->get_attrib_location("i_offset") : -1;
|
||||
GLint scales_id = (shader != nullptr) ? shader->get_attrib_location("i_scales") : -1;
|
||||
assert(offset_id != -1 && scales_id != -1);
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo));
|
||||
if (offset_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)0));
|
||||
glsafe(::glEnableVertexAttribArray(offset_id));
|
||||
glsafe(::glVertexAttribDivisor(offset_id, 1));
|
||||
}
|
||||
if (scales_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float))));
|
||||
glsafe(::glEnableVertexAttribArray(scales_id));
|
||||
glsafe(::glVertexAttribDivisor(scales_id, 1));
|
||||
}
|
||||
|
||||
for (const RenderData& data : m_render_data) {
|
||||
if (data.vbo_id == 0 || data.ibo_id == 0)
|
||||
continue;
|
||||
|
||||
GLenum mode;
|
||||
switch (data.type)
|
||||
{
|
||||
default:
|
||||
case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; }
|
||||
case PrimitiveType::Lines: { mode = GL_LINES; break; }
|
||||
case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; }
|
||||
case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; }
|
||||
}
|
||||
|
||||
if (shader != nullptr)
|
||||
shader->set_uniform("uniform_color", data.color);
|
||||
else
|
||||
glsafe(::glColor4fv(data.color.data()));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id));
|
||||
if (position_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0));
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
}
|
||||
if (normal_id != -1) {
|
||||
glsafe(::glVertexAttribPointer(normal_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(float))));
|
||||
glsafe(::glEnableVertexAttribArray(normal_id));
|
||||
}
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id));
|
||||
glsafe(::glDrawElementsInstanced(mode, static_cast<GLsizei>(data.indices_count), GL_UNSIGNED_INT, (const void*)0, instances_count));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
||||
if (normal_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(normal_id));
|
||||
if (position_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(position_id));
|
||||
}
|
||||
|
||||
if (scales_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(scales_id));
|
||||
if (offset_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(offset_id));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
void GLModel::send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices)
|
||||
{
|
||||
assert(data.vbo_id == 0);
|
||||
|
@ -598,5 +677,53 @@ GLModel::InitializationData straight_arrow(float tip_width, float tip_height, fl
|
|||
return data;
|
||||
}
|
||||
|
||||
GLModel::InitializationData diamond(int resolution)
|
||||
{
|
||||
resolution = std::max(4, resolution);
|
||||
|
||||
GLModel::InitializationData data;
|
||||
GLModel::InitializationData::Entity entity;
|
||||
entity.type = GLModel::PrimitiveType::Triangles;
|
||||
|
||||
const float step = 2.0f * float(PI) / float(resolution);
|
||||
|
||||
// positions
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
float ii = float(i) * step;
|
||||
entity.positions.emplace_back(0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f);
|
||||
}
|
||||
entity.positions.emplace_back(0.0f, 0.0f, 0.5f);
|
||||
entity.positions.emplace_back(0.0f, 0.0f, -0.5f);
|
||||
|
||||
// normals
|
||||
for (const Vec3f& v : entity.positions) {
|
||||
entity.normals.emplace_back(v.normalized());
|
||||
}
|
||||
|
||||
// triangles
|
||||
// top
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
entity.indices.push_back(i + 0);
|
||||
entity.indices.push_back(i + 1);
|
||||
entity.indices.push_back(resolution);
|
||||
}
|
||||
entity.indices.push_back(resolution - 1);
|
||||
entity.indices.push_back(0);
|
||||
entity.indices.push_back(resolution);
|
||||
|
||||
// bottom
|
||||
for (int i = 0; i < resolution; ++i) {
|
||||
entity.indices.push_back(i + 0);
|
||||
entity.indices.push_back(resolution + 1);
|
||||
entity.indices.push_back(i + 1);
|
||||
}
|
||||
entity.indices.push_back(resolution - 1);
|
||||
entity.indices.push_back(resolution + 1);
|
||||
entity.indices.push_back(0);
|
||||
|
||||
data.entities.emplace_back(entity);
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -72,6 +72,9 @@ namespace GUI {
|
|||
|
||||
void reset();
|
||||
void render() const;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
bool is_initialized() const { return !m_render_data.empty(); }
|
||||
|
||||
|
@ -100,6 +103,11 @@ namespace GUI {
|
|||
// used to render sidebar hints for position and scale
|
||||
GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness);
|
||||
|
||||
// create a diamond with the given resolution
|
||||
// the origin of the diamond is in its center
|
||||
// the diamond is contained into a box with size [1, 1, 1]
|
||||
GLModel::InitializationData diamond(int resolution);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -38,9 +38,17 @@ std::pair<bool, std::string> GLShadersManager::init()
|
|||
// used to render printbed
|
||||
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
|
||||
// used to render options in gcode preview
|
||||
valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" });
|
||||
if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20))
|
||||
valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" });
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
|
||||
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
|
||||
else {
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" });
|
||||
if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20))
|
||||
valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" });
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
// used to render extrusion and travel paths as lines in gcode preview
|
||||
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
|
||||
// used to render objects in 3d editor
|
||||
|
|
|
@ -1049,7 +1049,7 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||
|| event.GetKeyCode() == WXK_BACK
|
||||
#endif //__WXOSX__
|
||||
) {
|
||||
wxGetApp().plater()->remove_selected();
|
||||
remove();
|
||||
}
|
||||
else if (event.GetKeyCode() == WXK_F5)
|
||||
wxGetApp().plater()->reload_all_from_disk();
|
||||
|
@ -1702,8 +1702,7 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files
|
|||
snapshot_label += ", " + wxString::FromUTF8(paths[i].filename().string().c_str());
|
||||
|
||||
take_snapshot(snapshot_label);
|
||||
std::vector<size_t> res = wxGetApp().plater()->load_files(paths, true, false);
|
||||
if (!res.empty())
|
||||
if (! wxGetApp().plater()->load_files(paths, true, false).empty())
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
|
@ -1803,21 +1802,21 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
|
|||
cnv->get_gizmos_manager().reset_all_states();
|
||||
Plater::TakeSnapshot(plater, _L("Remove paint-on supports"));
|
||||
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
|
||||
mv->supported_facets.clear();
|
||||
mv->supported_facets.reset();
|
||||
break;
|
||||
|
||||
case InfoItemType::CustomSeam:
|
||||
cnv->get_gizmos_manager().reset_all_states();
|
||||
Plater::TakeSnapshot(plater, _L("Remove paint-on seam"));
|
||||
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
|
||||
mv->seam_facets.clear();
|
||||
mv->seam_facets.reset();
|
||||
break;
|
||||
|
||||
case InfoItemType::MmuSegmentation:
|
||||
cnv->get_gizmos_manager().reset_all_states();
|
||||
Plater::TakeSnapshot(plater, _L("Remove Multi Material painting"));
|
||||
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
|
||||
mv->mmu_segmentation_facets.clear();
|
||||
mv->mmu_segmentation_facets.reset();
|
||||
break;
|
||||
|
||||
case InfoItemType::Sinking:
|
||||
|
@ -1856,7 +1855,7 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
|
|||
if (is_layer_settings)
|
||||
layer_height = m_config->opt_float("layer_height");
|
||||
|
||||
m_config->clear();
|
||||
m_config->reset();
|
||||
|
||||
if (extruder >= 0)
|
||||
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
|
||||
|
@ -1932,7 +1931,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
|
|||
const auto last_volume = object->volumes[0];
|
||||
if (!last_volume->config.empty()) {
|
||||
object->config.apply(last_volume->config);
|
||||
last_volume->config.clear();
|
||||
last_volume->config.reset();
|
||||
|
||||
// update extruder color in ObjectList
|
||||
wxDataViewItem obj_item = m_objects_model->GetItemById(obj_idx);
|
||||
|
@ -2637,7 +2636,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
|
|||
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
|
||||
get_mesh_errors_count(obj_idx) > 0);
|
||||
|
||||
update_info_items(obj_idx, nullptr, true);
|
||||
update_info_items(obj_idx, nullptr, call_selection_changed);
|
||||
|
||||
// add volumes to the object
|
||||
if (model_object->volumes.size() > 1) {
|
||||
|
@ -3769,7 +3768,7 @@ void ObjectList::last_volume_is_deleted(const int obj_idx)
|
|||
auto volume = (*m_objects)[obj_idx]->volumes.front();
|
||||
|
||||
// clear volume's config values
|
||||
volume->config.clear();
|
||||
volume->config.reset();
|
||||
|
||||
// set a default extruder value, since user can't add it manually
|
||||
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
|
|
|
@ -191,7 +191,7 @@ void GLGizmoBase::render_grabbers(float size) const
|
|||
if (shader == nullptr)
|
||||
return;
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
for (int i = 0; i < (int)m_grabbers.size(); ++i) {
|
||||
if (m_grabbers[i].enabled)
|
||||
m_grabbers[i].render(m_hover_id == i, size);
|
||||
|
|
|
@ -136,7 +136,7 @@ void GLGizmoCut::on_render()
|
|||
if (shader == nullptr)
|
||||
return;
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
|
||||
m_grabbers[0].color = GrabberColor;
|
||||
m_grabbers[0].render(m_hover_id == 0, (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0));
|
||||
|
|
|
@ -131,6 +131,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
m_imgui->text("");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc["highlight_by_angle"] + ":");
|
||||
ImGui::AlignTextToFramePadding();
|
||||
std::string format_str = std::string("%.f") + I18N::translate_utf8("°",
|
||||
|
|
|
@ -19,6 +19,9 @@ protected:
|
|||
|
||||
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
|
||||
|
||||
std::string get_gizmo_entering_text() const override { return _u8L("Entering Paint-on supports"); }
|
||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Paint-on supports"); }
|
||||
|
||||
private:
|
||||
bool on_init() override;
|
||||
|
||||
|
|
|
@ -542,6 +542,7 @@ RENDER_AGAIN:
|
|||
|
||||
m_imgui->disabled_begin(! m_enable_hollowing);
|
||||
float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("offset"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
ImGui::PushItemWidth(window_width - settings_sliders_left);
|
||||
|
@ -558,6 +559,7 @@ RENDER_AGAIN:
|
|||
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
|
||||
|
||||
if (current_mode >= quality_mode) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("quality"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f");
|
||||
|
@ -574,6 +576,7 @@ RENDER_AGAIN:
|
|||
}
|
||||
|
||||
if (current_mode >= closing_d_mode) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("closing_distance"));
|
||||
ImGui::SameLine(settings_sliders_left);
|
||||
m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
|
||||
|
@ -621,6 +624,7 @@ RENDER_AGAIN:
|
|||
float diameter_upper_cap = 60.;
|
||||
if (m_new_hole_radius * 2.f > diameter_upper_cap)
|
||||
m_new_hole_radius = diameter_upper_cap / 2.f;
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("hole_diameter"));
|
||||
ImGui::SameLine(diameter_slider_left);
|
||||
ImGui::PushItemWidth(window_width - diameter_slider_left);
|
||||
|
@ -636,6 +640,7 @@ RENDER_AGAIN:
|
|||
bool edited = ImGui::IsItemEdited();
|
||||
bool deactivated = ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc["hole_depth"]);
|
||||
ImGui::SameLine(diameter_slider_left);
|
||||
m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false);
|
||||
|
@ -692,8 +697,10 @@ RENDER_AGAIN:
|
|||
// Following is rendered in both editing and non-editing mode:
|
||||
// m_imgui->text("");
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
}
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
|
|
|
@ -128,6 +128,9 @@ protected:
|
|||
|
||||
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
|
||||
|
||||
std::string get_gizmo_entering_text() const override { return _u8L("Entering Multimaterial painting"); }
|
||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Multimaterial painting"); }
|
||||
|
||||
size_t m_first_selected_extruder_idx = 0;
|
||||
size_t m_second_selected_extruder_idx = 1;
|
||||
std::vector<std::string> m_original_extruders_names;
|
||||
|
|
|
@ -141,7 +141,7 @@ void GLGizmoMove3D::on_render()
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
// draw grabber
|
||||
float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0);
|
||||
m_grabbers[m_hover_id].render(true, mean_size);
|
||||
|
@ -208,7 +208,7 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
|
|||
const_cast<GLModel*>(&m_vbo_cone)->set_color(-1, color);
|
||||
if (!picking) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
}
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
|
|
|
@ -47,20 +47,14 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
|
|||
plater->undo_redo_topmost_string_getter(plater->can_undo(), last_snapshot_name);
|
||||
|
||||
if (activate && !m_internal_stack_active) {
|
||||
std::string str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS
|
||||
? _u8L("Entering Paint-on supports")
|
||||
: _u8L("Entering Seam painting");
|
||||
if (last_snapshot_name != str)
|
||||
if (std::string str = this->get_gizmo_entering_text(); last_snapshot_name != str)
|
||||
Plater::TakeSnapshot(plater, str);
|
||||
plater->enter_gizmos_stack();
|
||||
m_internal_stack_active = true;
|
||||
}
|
||||
if (!activate && m_internal_stack_active) {
|
||||
plater->leave_gizmos_stack();
|
||||
std::string str = get_painter_type() == PainterGizmoType::SEAM
|
||||
? _u8L("Leaving Seam painting")
|
||||
: _u8L("Leaving Paint-on supports");
|
||||
if (last_snapshot_name != str)
|
||||
if (std::string str = this->get_gizmo_leaving_text(); last_snapshot_name != str)
|
||||
Plater::TakeSnapshot(plater, str);
|
||||
m_internal_stack_active = false;
|
||||
}
|
||||
|
|
|
@ -173,6 +173,9 @@ protected:
|
|||
|
||||
virtual wxString handle_snapshot_action_name(bool shift_down, Button button_down) const = 0;
|
||||
|
||||
virtual std::string get_gizmo_entering_text() const = 0;
|
||||
virtual std::string get_gizmo_leaving_text() const = 0;
|
||||
|
||||
friend class ::Slic3r::GUI::GLGizmoMmuSegmentation;
|
||||
};
|
||||
|
||||
|
|
|
@ -339,7 +339,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
|
|||
const_cast<GLModel*>(&m_cone)->set_color(-1, color);
|
||||
if (!picking) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
}
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
|
|
|
@ -236,7 +236,7 @@ void GLGizmoScale3D::on_render()
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
// draw grabbers
|
||||
m_grabbers[0].render(true, grabber_mean_size);
|
||||
m_grabbers[1].render(true, grabber_mean_size);
|
||||
|
@ -251,7 +251,7 @@ void GLGizmoScale3D::on_render()
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
// draw grabbers
|
||||
m_grabbers[2].render(true, grabber_mean_size);
|
||||
m_grabbers[3].render(true, grabber_mean_size);
|
||||
|
@ -266,7 +266,7 @@ void GLGizmoScale3D::on_render()
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
// draw grabbers
|
||||
m_grabbers[4].render(true, grabber_mean_size);
|
||||
m_grabbers[5].render(true, grabber_mean_size);
|
||||
|
@ -284,7 +284,7 @@ void GLGizmoScale3D::on_render()
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.1);
|
||||
shader->set_uniform("emission_factor", 0.1f);
|
||||
// draw grabbers
|
||||
for (int i = 6; i < 10; ++i) {
|
||||
m_grabbers[i].render(true, grabber_mean_size);
|
||||
|
|
|
@ -138,6 +138,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
|||
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(cursor_size_slider_left);
|
||||
ImGui::PushItemWidth(window_width - cursor_size_slider_left);
|
||||
|
@ -188,8 +189,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
|||
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
}
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
|
|
|
@ -20,6 +20,9 @@ protected:
|
|||
|
||||
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
|
||||
|
||||
std::string get_gizmo_entering_text() const override { return _u8L("Entering Seam painting"); }
|
||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Seam painting"); }
|
||||
|
||||
private:
|
||||
bool on_init() override;
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
ImGui::SameLine(m_gui_cfg->bottom_left_width);
|
||||
if (m_imgui->button(_L("Preview"))) {
|
||||
m_state = State::preview;
|
||||
// simplify but not aply on mesh
|
||||
// simplify but not apply on mesh
|
||||
process();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -207,15 +207,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
if (!m_is_valid_result) {
|
||||
m_state = State::close_on_end;
|
||||
process();
|
||||
} else {
|
||||
} else if (m_exist_preview) {
|
||||
// use preview and close
|
||||
if (m_exist_preview) {
|
||||
// fix hollowing, sla support points, modifiers, ...
|
||||
auto plater = wxGetApp().plater();
|
||||
plater->changed_mesh(m_obj_index);
|
||||
}
|
||||
after_apply();
|
||||
} else { // no changes made
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_imgui->disabled_begin(m_state == State::canceling);
|
||||
|
@ -237,18 +234,22 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
m_parent.reload_scene(true);
|
||||
// set m_state must be before close() !!!
|
||||
m_state = State::settings;
|
||||
if (close_on_end) {
|
||||
// fix hollowing, sla support points, modifiers, ...
|
||||
auto plater = wxGetApp().plater();
|
||||
plater->changed_mesh(m_obj_index);
|
||||
close();
|
||||
}
|
||||
if (close_on_end) after_apply();
|
||||
|
||||
// Fix warning icon in object list
|
||||
wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::after_apply() {
|
||||
// set flag to NOT revert changes when switch GLGizmoBase::m_state
|
||||
m_exist_preview = false;
|
||||
// fix hollowing, sla support points, modifiers, ...
|
||||
auto plater = wxGetApp().plater();
|
||||
plater->changed_mesh(m_obj_index);
|
||||
close();
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::close() {
|
||||
// close gizmo == open it again
|
||||
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
|
||||
|
@ -282,11 +283,11 @@ void GLGizmoSimplify::process()
|
|||
}
|
||||
};
|
||||
|
||||
std::function<void(int)> statusfn = [this](int percent) {
|
||||
int64_t last = 0;
|
||||
std::function<void(int)> statusfn = [this, &last](int percent) {
|
||||
m_progress = percent;
|
||||
|
||||
// check max 4fps
|
||||
static int64_t last = 0;
|
||||
int64_t now = m_parent.timestamp_now();
|
||||
if ((now - last) < 250) return;
|
||||
last = now;
|
||||
|
|
|
@ -32,6 +32,7 @@ protected:
|
|||
virtual void on_set_state() override;
|
||||
|
||||
private:
|
||||
void after_apply();
|
||||
void close();
|
||||
void process();
|
||||
void set_its(indexed_triangle_set &its);
|
||||
|
|
|
@ -169,7 +169,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
const_cast<GLModel*>(&m_cone)->set_color(-1, render_color);
|
||||
const_cast<GLModel*>(&m_sphere)->set_color(-1, render_color);
|
||||
if (shader && !picking)
|
||||
shader->set_uniform("emission_factor", 0.5);
|
||||
shader->set_uniform("emission_factor", 0.5f);
|
||||
|
||||
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
||||
glsafe(::glPushMatrix());
|
||||
|
@ -224,7 +224,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
render_color[3] = 0.7f;
|
||||
const_cast<GLModel*>(&m_cylinder)->set_color(-1, render_color);
|
||||
if (shader)
|
||||
shader->set_uniform("emission_factor", 0.5);
|
||||
shader->set_uniform("emission_factor", 0.5f);
|
||||
for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
|
||||
if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
|
||||
continue;
|
||||
|
@ -786,8 +786,7 @@ RENDER_AGAIN:
|
|||
|
||||
// Following is rendered in both editing and non-editing mode:
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
{
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
}
|
||||
|
|
|
@ -620,7 +620,7 @@ void MainFrame::update_title()
|
|||
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
|
||||
if (!dirty_marker.empty() || !project.empty()) {
|
||||
if (!dirty_marker.empty() && project.empty())
|
||||
project = _("Untitled");
|
||||
project = _L("Untitled");
|
||||
title = dirty_marker + project + " - ";
|
||||
}
|
||||
}
|
||||
|
@ -819,7 +819,7 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab)
|
|||
|
||||
bool MainFrame::can_start_new_project() const
|
||||
{
|
||||
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
|
||||
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || GetTitle().StartsWith('*') || !m_plater->model().objects.empty());
|
||||
}
|
||||
|
||||
bool MainFrame::can_save() const
|
||||
|
|
|
@ -106,7 +106,6 @@ void MeshClipper::recalculate_triangles()
|
|||
Transform3d tr = Transform3d::Identity();
|
||||
tr.rotate(q);
|
||||
tr = m_trafo.get_matrix() * tr;
|
||||
height_mesh += 0.001f; // to avoid z-fighting
|
||||
|
||||
if (m_limiting_plane != ClippingPlane::ClipsNothing())
|
||||
{
|
||||
|
@ -165,6 +164,8 @@ void MeshClipper::recalculate_triangles()
|
|||
|
||||
m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.);
|
||||
|
||||
tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting
|
||||
|
||||
m_vertex_array.release_geometry();
|
||||
for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) {
|
||||
m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up);
|
||||
|
|
|
@ -112,7 +112,7 @@ public:
|
|||
// The class references extern TriangleMesh, which must stay alive
|
||||
// during MeshRaycaster existence.
|
||||
MeshRaycaster(const TriangleMesh& mesh)
|
||||
: m_emesh(mesh)
|
||||
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
|
||||
{
|
||||
m_normals.reserve(mesh.stl.facet_start.size());
|
||||
for (const stl_facet& facet : mesh.stl.facet_start)
|
||||
|
|
|
@ -1369,7 +1369,18 @@ void NotificationManager::push_hint_notification(bool open_next)
|
|||
}
|
||||
|
||||
NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 300, "" };
|
||||
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), 0);
|
||||
// from user - open now
|
||||
if (!open_next) {
|
||||
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), 0);
|
||||
stop_delayed_notifications_of_type(NotificationType::DidYouKnowHint);
|
||||
// at startup - delay for half a second to let other notification pop up, than try every 30 seconds
|
||||
// show only if no notifications are shown
|
||||
} else {
|
||||
auto condition = [this]() {
|
||||
return this->get_notification_count() == 0;
|
||||
};
|
||||
push_delayed_notification(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), condition, 500, 30000);
|
||||
}
|
||||
}
|
||||
|
||||
bool NotificationManager::is_hint_notification_open()
|
||||
|
@ -1425,6 +1436,28 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
|
|||
}
|
||||
}
|
||||
|
||||
void NotificationManager::push_delayed_notification(std::unique_ptr<NotificationManager::PopNotification> notification, std::function<bool(void)> condition_callback, int64_t initial_delay, int64_t delay_interval)
|
||||
{
|
||||
if (initial_delay == 0 && condition_callback()) {
|
||||
if( push_notification_data(std::move(notification), 0))
|
||||
return;
|
||||
}
|
||||
m_waiting_notifications.emplace_back(std::move(notification), condition_callback, initial_delay == 0 ? delay_interval : initial_delay, delay_interval);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(initial_delay == 0 ? delay_interval : initial_delay);
|
||||
}
|
||||
|
||||
void NotificationManager::stop_delayed_notifications_of_type(const NotificationType type)
|
||||
{
|
||||
for (auto it = m_waiting_notifications.begin(); it != m_waiting_notifications.end();) {
|
||||
if ((*it).notification->get_type() == type) {
|
||||
it = m_waiting_notifications.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width)
|
||||
{
|
||||
sort_notifications();
|
||||
|
@ -1477,6 +1510,26 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas)
|
|||
++it;
|
||||
}
|
||||
|
||||
// delayed notifications
|
||||
for (auto it = m_waiting_notifications.begin(); it != m_waiting_notifications.end();) {
|
||||
// substract time
|
||||
if ((*it).remaining_time > 0)
|
||||
(*it).remaining_time -= time_since_render;
|
||||
if ((*it).remaining_time <= 0) {
|
||||
if ((*it).condition_callback()) { // push notification, erase it from waiting list (frame is scheduled by push)
|
||||
(*it).notification->reset_timer();
|
||||
if (push_notification_data(std::move((*it).notification), 0)) {
|
||||
it = m_waiting_notifications.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// not possible to push, delay for delay_interval
|
||||
(*it).remaining_time = (*it).delay_interval;
|
||||
}
|
||||
next_render = std::min<int64_t>(next_render, (*it).remaining_time);
|
||||
++it;
|
||||
}
|
||||
|
||||
// request next frame in future
|
||||
if (next_render < max)
|
||||
canvas.schedule_extra_frame(int(next_render));
|
||||
|
@ -1569,6 +1622,15 @@ void NotificationManager::device_ejected()
|
|||
notification->close();
|
||||
}
|
||||
}
|
||||
size_t NotificationManager::get_notification_count() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_state() != PopNotification::EState::Hidden)
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}//namespace GUI
|
||||
}//namespace Slic3r
|
||||
|
|
|
@ -190,6 +190,8 @@ public:
|
|||
void set_move_from_overlay(bool move) { m_move_from_overlay = move; }
|
||||
// perform update_state on each notification and ask for more frames if needed, return true for render needed
|
||||
bool update_notifications(GLCanvas3D& canvas);
|
||||
// returns number of all notifications shown
|
||||
size_t get_notification_count() const;
|
||||
private:
|
||||
// duration 0 means not disapearing
|
||||
struct NotificationData {
|
||||
|
@ -262,6 +264,8 @@ private:
|
|||
EState get_state() const { return m_state; }
|
||||
bool is_hovered() const { return m_state == EState::Hovered; }
|
||||
void set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; }
|
||||
// set start of notification to now. Used by delayed notifications
|
||||
void reset_timer() { m_notification_start = GLCanvas3D::timestamp_now(); m_state = EState::Shown; }
|
||||
protected:
|
||||
// Call after every size change
|
||||
virtual void init();
|
||||
|
@ -515,10 +519,34 @@ private:
|
|||
// in HintNotification.hpp
|
||||
class HintNotification;
|
||||
|
||||
// Data of waiting notifications
|
||||
struct DelayedNotification
|
||||
{
|
||||
std::unique_ptr<PopNotification> notification;
|
||||
std::function<bool(void)> condition_callback;
|
||||
int64_t remaining_time;
|
||||
int64_t delay_interval;
|
||||
|
||||
DelayedNotification(std::unique_ptr<PopNotification> n, std::function<bool(void)> cb, int64_t r, int64_t d)
|
||||
: notification(std::move(n))
|
||||
, condition_callback(cb)
|
||||
, remaining_time(r)
|
||||
, delay_interval(d)
|
||||
{}
|
||||
};
|
||||
|
||||
//pushes notification into the queue of notifications that are rendered
|
||||
//can be used to create custom notification
|
||||
bool push_notification_data(const NotificationData& notification_data, int timestamp);
|
||||
bool push_notification_data(std::unique_ptr<NotificationManager::PopNotification> notification, int timestamp);
|
||||
// Delayed notifications goes first to the m_waiting_notifications vector and only after remaining time is <= 0
|
||||
// and condition callback is success, notification is regular pushed from update function.
|
||||
// Otherwise another delay interval waiting. Timestamp is 0.
|
||||
// Note that notification object is constructed when being added to the waiting list, but there are no updates called on it and its timer is reset at regular push.
|
||||
// Also note that no control of same notification is done during push_delayed_notification but if waiting notif fails to push, it continues waiting.
|
||||
void push_delayed_notification(std::unique_ptr<NotificationManager::PopNotification> notification, std::function<bool(void)> condition_callback, int64_t initial_delay, int64_t delay_interval);
|
||||
// Removes all notifications of type from m_waiting_notifications
|
||||
void stop_delayed_notifications_of_type(const NotificationType type);
|
||||
//finds older notification of same type and moves it to the end of queue. returns true if found
|
||||
bool activate_existing(const NotificationManager::PopNotification* notification);
|
||||
// Put the more important notifications to the bottom of the list.
|
||||
|
@ -531,6 +559,8 @@ private:
|
|||
// Cache of IDs to identify and reuse ImGUI windows.
|
||||
NotificationIDProvider m_id_provider;
|
||||
std::deque<std::unique_ptr<PopNotification>> m_pop_notifications;
|
||||
// delayed waiting notifications, first is remaining time
|
||||
std::deque<DelayedNotification> m_waiting_notifications;
|
||||
//timestamps used for slicing finished - notification could be gone so it needs to be stored here
|
||||
std::unordered_set<int> m_used_timestamps;
|
||||
// True if G-code preview is active. False if the Plater is active.
|
||||
|
|
|
@ -1834,7 +1834,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
, main_frame(main_frame)
|
||||
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "brim_offset", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
|
@ -2221,7 +2221,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
std::vector<size_t> obj_idxs;
|
||||
|
||||
for (size_t i = 0; i < input_files.size(); ++i) {
|
||||
#ifdef _WIN32
|
||||
auto path = input_files[i];
|
||||
// On Windows, we swap slashes to back slashes, see GH #6803 as read_from_file() does not understand slashes on Windows thus it assignes full path to names of loaded objects.
|
||||
path.make_preferred();
|
||||
#else // _WIN32
|
||||
// Don't make a copy on Posix. Slash is a path separator, back slashes are not accepted as a substitute.
|
||||
const auto &path = input_files[i];
|
||||
#endif // _WIN32
|
||||
const auto filename = path.filename();
|
||||
dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
|
||||
dlg.Fit();
|
||||
|
@ -2336,9 +2343,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
else {
|
||||
model = Slic3r::Model::read_from_file(path.string(), nullptr, nullptr, only_if(load_config, Model::LoadAttribute::CheckVersion));
|
||||
for (auto obj : model.objects)
|
||||
if (obj->name.empty() ||
|
||||
obj->name.find_first_of("/") != std::string::npos) // When file is imported from Fusion360 the path containes "/" instead of "\\" (see https://github.com/prusa3d/PrusaSlicer/issues/6803)
|
||||
// But read_from_file doesn't support that direction separator and as a result object name containes full path
|
||||
if (obj->name.empty())
|
||||
obj->name = fs::path(obj->input_file).filename().string();
|
||||
}
|
||||
} catch (const ConfigurationError &e) {
|
||||
|
@ -2457,7 +2462,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
}
|
||||
|
||||
if (load_model) {
|
||||
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().string());
|
||||
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().make_preferred().string());
|
||||
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
|
||||
statusbar()->set_status_text(_L("Loaded"));
|
||||
}
|
||||
|
@ -2915,6 +2920,7 @@ void Plater::priv::update_print_volume_state()
|
|||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt<ConfigOptionPoints>("bed_shape")->values));
|
||||
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height"))));
|
||||
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
|
||||
print_volume.offset(BedEpsilon);
|
||||
print_volume.min(2) = -1e10;
|
||||
this->q->model().update_print_volume_state(print_volume);
|
||||
}
|
||||
|
@ -4786,10 +4792,7 @@ void Plater::load_project(const wxString& filename)
|
|||
|
||||
p->reset();
|
||||
|
||||
std::vector<fs::path> input_paths;
|
||||
input_paths.push_back(into_path(filename));
|
||||
|
||||
if (! load_files(input_paths).empty()) {
|
||||
if (! load_files({ into_path(filename) }).empty()) {
|
||||
// At least one file was loaded.
|
||||
p->set_project_filename(filename);
|
||||
reset_project_dirty_initial_presets();
|
||||
|
@ -4806,7 +4809,7 @@ void Plater::add_model(bool imperial_units/* = false*/)
|
|||
|
||||
std::vector<fs::path> paths;
|
||||
for (const auto &file : input_files)
|
||||
paths.push_back(into_path(file));
|
||||
paths.emplace_back(into_path(file));
|
||||
|
||||
wxString snapshot_label;
|
||||
assert(! paths.empty());
|
||||
|
@ -4839,12 +4842,8 @@ void Plater::extract_config_from_project()
|
|||
wxString input_file;
|
||||
wxGetApp().load_project(this, input_file);
|
||||
|
||||
if (input_file.empty())
|
||||
return;
|
||||
|
||||
std::vector<fs::path> input_paths;
|
||||
input_paths.push_back(into_path(input_file));
|
||||
load_files(input_paths, false, true);
|
||||
if (! input_file.empty())
|
||||
load_files({ into_path(input_file) }, false, true);
|
||||
}
|
||||
|
||||
void Plater::load_gcode()
|
||||
|
@ -5075,15 +5074,11 @@ bool Plater::load_files(const wxArrayString& filenames)
|
|||
}
|
||||
case LoadType::LoadGeometry: {
|
||||
Plater::TakeSnapshot snapshot(this, _L("Import Object"));
|
||||
std::vector<fs::path> in_paths;
|
||||
in_paths.emplace_back(*it);
|
||||
load_files(in_paths, true, false);
|
||||
load_files({ *it }, true, false);
|
||||
break;
|
||||
}
|
||||
case LoadType::LoadConfig: {
|
||||
std::vector<fs::path> in_paths;
|
||||
in_paths.emplace_back(*it);
|
||||
load_files(in_paths, false, true);
|
||||
load_files({ *it }, false, true);
|
||||
break;
|
||||
}
|
||||
case LoadType::Unknown : {
|
||||
|
@ -5158,8 +5153,11 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo
|
|||
|
||||
void Plater::remove_selected()
|
||||
{
|
||||
if (p->get_selection().is_empty())
|
||||
return;
|
||||
|
||||
Plater::TakeSnapshot snapshot(this, _L("Delete Selected Objects"));
|
||||
this->p->view3D->delete_selected();
|
||||
p->view3D->delete_selected();
|
||||
}
|
||||
|
||||
void Plater::increase_instances(size_t num)
|
||||
|
@ -6221,9 +6219,9 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
|||
bool paint_removed = false;
|
||||
for (ModelVolume* mv : mo->volumes) {
|
||||
paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty();
|
||||
mv->supported_facets.clear();
|
||||
mv->seam_facets.clear();
|
||||
mv->mmu_segmentation_facets.clear();
|
||||
mv->supported_facets.reset();
|
||||
mv->seam_facets.reset();
|
||||
mv->mmu_segmentation_facets.reset();
|
||||
}
|
||||
if (paint_removed) {
|
||||
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
|
||||
|
@ -6585,6 +6583,11 @@ bool Plater::is_render_statistic_dialog_visible() const
|
|||
return p->show_render_statistic_dialog;
|
||||
}
|
||||
|
||||
|
||||
Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name)
|
||||
: TakeSnapshot(plater, from_u8(snapshot_name)) {}
|
||||
|
||||
|
||||
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
|
||||
bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
|
||||
{
|
||||
|
|
|
@ -383,10 +383,11 @@ public:
|
|||
Plater *m_plater;
|
||||
};
|
||||
|
||||
// ROII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
|
||||
// RAII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
|
||||
class TakeSnapshot
|
||||
{
|
||||
public:
|
||||
TakeSnapshot(Plater *plater, const std::string &snapshot_name);
|
||||
TakeSnapshot(Plater *plater, const wxString &snapshot_name) : m_plater(plater)
|
||||
{
|
||||
m_plater->take_snapshot(snapshot_name);
|
||||
|
|
|
@ -1874,7 +1874,7 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con
|
|||
const_cast<GLModel*>(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis));
|
||||
GLShaderProgram* shader = wxGetApp().get_current_shader();
|
||||
if (shader != nullptr)
|
||||
shader->set_uniform("emission_factor", 0.0);
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
|
||||
glsafe(::glTranslated(0.0, 5.0, 0.0));
|
||||
m_arrow.render();
|
||||
|
|
|
@ -1509,7 +1509,7 @@ void TabPrint::build()
|
|||
optgroup = page->new_optgroup(L("Brim"));
|
||||
optgroup->append_single_option_line("brim_type", category_path + "brim");
|
||||
optgroup->append_single_option_line("brim_width", category_path + "brim");
|
||||
optgroup->append_single_option_line("brim_offset", category_path + "brim");
|
||||
optgroup->append_single_option_line("brim_separation", category_path + "brim");
|
||||
|
||||
page = add_options_page(L("Support material"), "support");
|
||||
category_path = "support-material_1698#";
|
||||
|
@ -1565,12 +1565,14 @@ void TabPrint::build()
|
|||
|
||||
optgroup = page->new_optgroup(L("Modifiers"));
|
||||
optgroup->append_single_option_line("first_layer_speed");
|
||||
optgroup->append_single_option_line("first_layer_speed_over_raft");
|
||||
|
||||
optgroup = page->new_optgroup(L("Acceleration control (advanced)"));
|
||||
optgroup->append_single_option_line("perimeter_acceleration");
|
||||
optgroup->append_single_option_line("infill_acceleration");
|
||||
optgroup->append_single_option_line("bridge_acceleration");
|
||||
optgroup->append_single_option_line("first_layer_acceleration");
|
||||
optgroup->append_single_option_line("first_layer_acceleration_over_raft");
|
||||
optgroup->append_single_option_line("default_acceleration");
|
||||
|
||||
optgroup = page->new_optgroup(L("Autospeed (advanced)"));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue