mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 15:44:12 -06:00
Merge branch 'master' into lm_drilling_backend_rebased
This commit is contained in:
commit
a1d4dab999
63 changed files with 3891 additions and 3327 deletions
|
@ -233,15 +233,30 @@ cmake_policy(SET CMP0011 NEW)
|
|||
find_package(CGAL REQUIRED)
|
||||
cmake_policy(POP)
|
||||
|
||||
add_library(libslic3r_cgal OBJECT MeshBoolean.cpp MeshBoolean.hpp)
|
||||
target_include_directories(libslic3r_cgal PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
$<TARGET_PROPERTY:libigl,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
target_compile_definitions(libslic3r_cgal PRIVATE
|
||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_COMPILE_DEFINITIONS>)
|
||||
target_compile_options(libslic3r_cgal PRIVATE
|
||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_COMPILE_OPTIONS>)
|
||||
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp)
|
||||
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
||||
# (-frounding-math) still propagate to dependent libs which is not desired.
|
||||
get_target_property(_cgal_tgt CGAL::CGAL ALIASED_TARGET)
|
||||
if (NOT TARGET ${_cgal_tgt})
|
||||
set (_cgal_tgt CGAL::CGAL)
|
||||
endif ()
|
||||
get_target_property(_opts ${_cgal_tgt} INTERFACE_COMPILE_OPTIONS)
|
||||
if (_opts)
|
||||
set(_opts_bad "${_opts}")
|
||||
set(_opts_good "${_opts}")
|
||||
list(FILTER _opts_bad INCLUDE REGEX frounding-math)
|
||||
list(FILTER _opts_good EXCLUDE REGEX frounding-math)
|
||||
set_target_properties(${_cgal_tgt} PROPERTIES INTERFACE_COMPILE_OPTIONS "${_opts_good}")
|
||||
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
|
||||
endif()
|
||||
|
||||
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl)
|
||||
|
||||
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
|
||||
target_compile_definitions(libslic3r_cgal PRIVATE CGAL_DO_NOT_USE_MPZF)
|
||||
endif ()
|
||||
|
||||
encoding_check(libslic3r)
|
||||
|
||||
|
@ -263,7 +278,7 @@ target_link_libraries(libslic3r
|
|||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_LINK_LIBRARIES>
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
|
@ -282,5 +297,3 @@ endif()
|
|||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
||||
target_sources(libslic3r PRIVATE $<TARGET_OBJECTS:libslic3r_cgal>)
|
||||
|
|
|
@ -958,7 +958,7 @@ namespace DoExport {
|
|||
skirts.emplace_back(std::move(s));
|
||||
}
|
||||
ooze_prevention.enable = true;
|
||||
ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), scale_(3.f)).front().equally_spaced_points(scale_(10.));
|
||||
ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.)));
|
||||
#if 0
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
|
@ -1091,7 +1091,7 @@ namespace DoExport {
|
|||
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
|
||||
{
|
||||
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
|
||||
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); });
|
||||
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size()(2) < po2->size()(2); });
|
||||
std::vector<const PrintInstance*> instances;
|
||||
instances.reserve(objects.size());
|
||||
for (const PrintObject *object : objects)
|
||||
|
|
|
@ -252,46 +252,6 @@ template<class T> struct remove_cvref
|
|||
|
||||
template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
|
||||
template<class T> using DefaultContainer = std::vector<T>;
|
||||
|
||||
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
|
||||
template<class T, class I, template<class> class Container = DefaultContainer>
|
||||
inline Container<remove_cvref_t<T>> linspace(const T &start,
|
||||
const T &stop,
|
||||
const I &n)
|
||||
{
|
||||
Container<remove_cvref_t<T>> vals(n, T());
|
||||
|
||||
T stride = (stop - start) / n;
|
||||
size_t i = 0;
|
||||
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
|
||||
return start + i++ * stride;
|
||||
});
|
||||
|
||||
return vals;
|
||||
}
|
||||
|
||||
/// A set of equidistant values starting from 'start' (inclusive), ending
|
||||
/// in the closest multiple of 'stride' less than or equal to 'end' and
|
||||
/// leaving 'stride' space between each value.
|
||||
/// Very similar to Matlab [start:stride:end] notation.
|
||||
template<class T, template<class> class Container = DefaultContainer>
|
||||
inline Container<remove_cvref_t<T>> grid(const T &start,
|
||||
const T &stop,
|
||||
const T &stride)
|
||||
{
|
||||
Container<remove_cvref_t<T>>
|
||||
vals(size_t(std::ceil((stop - start) / stride)), T());
|
||||
|
||||
int i = 0;
|
||||
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
|
||||
return start + i++ * stride;
|
||||
});
|
||||
|
||||
return vals;
|
||||
}
|
||||
|
||||
|
||||
// A shorter C++14 style form of the enable_if metafunction
|
||||
template<bool B, class T>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
@ -392,6 +352,56 @@ inline IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
|
||||
template<class T, class I>
|
||||
inline std::vector<T> linspace_vector(const ArithmeticOnly<T> &start,
|
||||
const T &stop,
|
||||
const IntegerOnly<I> &n)
|
||||
{
|
||||
std::vector<T> vals(n, T());
|
||||
|
||||
T stride = (stop - start) / n;
|
||||
size_t i = 0;
|
||||
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
|
||||
return start + i++ * stride;
|
||||
});
|
||||
|
||||
return vals;
|
||||
}
|
||||
|
||||
template<size_t N, class T>
|
||||
inline std::array<ArithmeticOnly<T>, N> linspace_array(const T &start, const T &stop)
|
||||
{
|
||||
std::array<T, N> vals = {T()};
|
||||
|
||||
T stride = (stop - start) / N;
|
||||
size_t i = 0;
|
||||
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
|
||||
return start + i++ * stride;
|
||||
});
|
||||
|
||||
return vals;
|
||||
}
|
||||
|
||||
/// A set of equidistant values starting from 'start' (inclusive), ending
|
||||
/// in the closest multiple of 'stride' less than or equal to 'end' and
|
||||
/// leaving 'stride' space between each value.
|
||||
/// Very similar to Matlab [start:stride:end] notation.
|
||||
template<class T>
|
||||
inline std::vector<ArithmeticOnly<T>> grid(const T &start,
|
||||
const T &stop,
|
||||
const T &stride)
|
||||
{
|
||||
std::vector<T> vals(size_t(std::ceil((stop - start) / stride)), T());
|
||||
|
||||
int i = 0;
|
||||
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
|
||||
return start + i++ * stride;
|
||||
});
|
||||
|
||||
return vals;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // MTUTILS_HPP
|
||||
|
|
|
@ -111,7 +111,7 @@ static TriangleMesh cgal_to_triangle_mesh(const _CGALMesh &cgalmesh)
|
|||
auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face));
|
||||
int i = 0;
|
||||
Vec3crd trface;
|
||||
for (auto v : vtc) trface(i++) = int(v.idx());
|
||||
for (auto v : vtc) trface(i++) = static_cast<unsigned>(v);
|
||||
facets.emplace_back(trface);
|
||||
}
|
||||
|
||||
|
|
|
@ -907,10 +907,8 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
|
|||
|
||||
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
{
|
||||
if (v->is_model_part())
|
||||
m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
}
|
||||
}
|
||||
return m_raw_bounding_box;
|
||||
}
|
||||
|
@ -1115,7 +1113,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
if (keep_upper) {
|
||||
upper->set_model(nullptr);
|
||||
upper->sla_support_points.clear();
|
||||
lower->sla_drain_holes.clear();
|
||||
upper->sla_drain_holes.clear();
|
||||
upper->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
upper->clear_volumes();
|
||||
upper->input_file = "";
|
||||
|
|
|
@ -674,6 +674,7 @@ public:
|
|||
set_rotation(Z, rotation);
|
||||
set_offset(X, unscale<double>(offs(X)));
|
||||
set_offset(Y, unscale<double>(offs(Y)));
|
||||
this->object->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
@ -836,7 +836,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
// Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked.
|
||||
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
|
||||
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
|
||||
|
@ -899,10 +899,14 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
||||
model_object.instances.back()->set_model_object(&model_object);
|
||||
}
|
||||
} else {
|
||||
// Just synchronize the content of the instances. This avoids memory allocation and it does not invalidate ModelInstance pointers,
|
||||
// which may be accessed by G-code export in the meanwhile to deduce sequential print order.
|
||||
auto new_instance = model_object_new.instances.begin();
|
||||
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
|
||||
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
|
||||
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
|
||||
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
|
||||
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
|
||||
model_object.invalidate_bounding_box();
|
||||
// Synchronize the content of instances.
|
||||
auto new_instance = model_object_new.instances.begin();
|
||||
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
|
||||
(*old_instance)->set_transformation((*new_instance)->get_transformation());
|
||||
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
|
||||
|
@ -1197,7 +1201,7 @@ std::string Print::validate() const
|
|||
{
|
||||
std::vector<coord_t> object_height;
|
||||
for (const PrintObject *object : m_objects)
|
||||
object_height.insert(object_height.end(), object->instances().size(), object->size(2));
|
||||
object_height.insert(object_height.end(), object->instances().size(), object->size()(2));
|
||||
std::sort(object_height.begin(), object_height.end());
|
||||
// Ignore the tallest *copy* (this is why we repeat height for all of them):
|
||||
// it will be printed as last one so its height doesn't matter.
|
||||
|
@ -1429,7 +1433,7 @@ BoundingBox Print::bounding_box() const
|
|||
for (const PrintObject *object : m_objects)
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
bb.merge(instance.shift);
|
||||
bb.merge(instance.shift + to_2d(object->size));
|
||||
bb.merge(instance.shift + to_2d(object->size()));
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
|
|
@ -120,17 +120,17 @@ public:
|
|||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
bool typed_slices;
|
||||
|
||||
Vec3crd size; // XYZ in scaled coordinates
|
||||
|
||||
// XYZ in scaled coordinates
|
||||
const Vec3crd& size() const { return m_size; }
|
||||
const PrintObjectConfig& config() const { return m_config; }
|
||||
const LayerPtrs& layers() const { return m_layers; }
|
||||
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
||||
const Transform3d& trafo() const { return m_trafo; }
|
||||
const PrintInstances& instances() const { return m_instances; }
|
||||
const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
|
||||
const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size().x() / 2, this->size().y() / 2); }
|
||||
|
||||
// since the object is aligned to origin, bounding box coincides with size
|
||||
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
|
||||
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size())); }
|
||||
|
||||
// adds region_id, too, if necessary
|
||||
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
|
||||
|
@ -235,6 +235,8 @@ private:
|
|||
void combine_infill();
|
||||
void _generate_support_material();
|
||||
|
||||
// XYZ in scaled coordinates
|
||||
Vec3crd m_size;
|
||||
PrintObjectConfig m_config;
|
||||
// Translation in Z + Rotation + Scaling / Mirroring.
|
||||
Transform3d m_trafo = Transform3d::Identity();
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Slic3r {
|
|||
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
|
||||
PrintObjectBaseWithState(print, model_object),
|
||||
typed_slices(false),
|
||||
size(Vec3crd::Zero())
|
||||
m_size(Vec3crd::Zero())
|
||||
{
|
||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||
const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box();
|
||||
m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
|
||||
// Scale the object size and store it
|
||||
this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
||||
this->m_size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
||||
}
|
||||
|
||||
if (add_instances) {
|
||||
|
@ -73,6 +73,8 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||
|
||||
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
||||
{
|
||||
for (PrintInstance &i : instances)
|
||||
i.shift += m_copies_shift;
|
||||
// Invalidate and set copies.
|
||||
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
|
||||
bool equal_length = instances.size() == m_instances.size();
|
||||
|
@ -83,7 +85,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
|||
if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) ||
|
||||
(! equal_length && m_print->invalidate_step(psWipeTower)))
|
||||
status = PrintBase::APPLY_STATUS_INVALIDATED;
|
||||
m_instances = instances;
|
||||
m_instances = std::move(instances);
|
||||
for (PrintInstance &i : m_instances)
|
||||
i.print_object = this;
|
||||
}
|
||||
|
@ -1448,7 +1450,7 @@ void PrintObject::update_slicing_parameters()
|
|||
{
|
||||
if (! m_slicing_params.valid)
|
||||
m_slicing_params = SlicingParameters::create_from_config(
|
||||
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
|
||||
this->print()->config(), m_config, unscale<double>(this->size()(2)), this->object_extruders());
|
||||
}
|
||||
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <igl/ray_mesh_intersect.h>
|
||||
#include <igl/point_mesh_squared_distance.h>
|
||||
#include <igl/remove_duplicate_vertices.h>
|
||||
#include <igl/collapse_small_triangles.h>
|
||||
#include <igl/signed_distance.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@ -194,17 +195,12 @@ public:
|
|||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
};
|
||||
|
||||
EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
|
||||
static const double dEPS = 1e-6;
|
||||
|
||||
static const constexpr double MESH_EPS = 1e-6;
|
||||
|
||||
void to_eigen_mesh(const TriangleMesh &tmesh, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
|
||||
{
|
||||
const stl_file& stl = tmesh.stl;
|
||||
|
||||
auto&& bb = tmesh.bounding_box();
|
||||
m_ground_level += bb.min(Z);
|
||||
|
||||
Eigen::MatrixXd V;
|
||||
Eigen::MatrixXi F;
|
||||
|
||||
V.resize(3*stl.stats.number_of_facets, 3);
|
||||
F.resize(stl.stats.number_of_facets, 3);
|
||||
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) {
|
||||
|
@ -217,9 +213,37 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
|
|||
F(i, 2) = int(3*i+2);
|
||||
}
|
||||
|
||||
// We will convert this to a proper 3d mesh with no duplicate points.
|
||||
Eigen::VectorXi SVI, SVJ;
|
||||
igl::remove_duplicate_vertices(V, F, dEPS, m_V, SVI, SVJ, m_F);
|
||||
if (!tmesh.has_shared_vertices())
|
||||
{
|
||||
Eigen::MatrixXd rV;
|
||||
Eigen::MatrixXi rF;
|
||||
// We will convert this to a proper 3d mesh with no duplicate points.
|
||||
Eigen::VectorXi SVI, SVJ;
|
||||
igl::remove_duplicate_vertices(V, F, MESH_EPS, rV, SVI, SVJ, rF);
|
||||
V = std::move(rV);
|
||||
F = std::move(rF);
|
||||
}
|
||||
}
|
||||
|
||||
void to_triangle_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, TriangleMesh &out)
|
||||
{
|
||||
Pointf3s points(size_t(V.rows()));
|
||||
std::vector<Vec3crd> facets(size_t(F.rows()));
|
||||
|
||||
for (Eigen::Index i = 0; i < V.rows(); ++i)
|
||||
points[size_t(i)] = V.row(i);
|
||||
|
||||
for (Eigen::Index i = 0; i < F.rows(); ++i)
|
||||
facets[size_t(i)] = F.row(i);
|
||||
|
||||
out = {points, facets};
|
||||
}
|
||||
|
||||
EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
|
||||
auto&& bb = tmesh.bounding_box();
|
||||
m_ground_level += bb.min(Z);
|
||||
|
||||
to_eigen_mesh(tmesh, m_V, m_F);
|
||||
|
||||
// Build the AABB accelaration tree
|
||||
m_aabb->init(m_V, m_F);
|
||||
|
@ -262,6 +286,10 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other)
|
|||
m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this;
|
||||
}
|
||||
|
||||
EigenMesh3D &EigenMesh3D::operator=(EigenMesh3D &&other) = default;
|
||||
|
||||
EigenMesh3D::EigenMesh3D(EigenMesh3D &&other) = default;
|
||||
|
||||
EigenMesh3D::hit_result
|
||||
EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
|
||||
{
|
||||
|
|
|
@ -12,6 +12,9 @@ namespace sla {
|
|||
|
||||
struct Contour3D;
|
||||
|
||||
void to_eigen_mesh(const TriangleMesh &mesh, Eigen::MatrixXd &V, Eigen::MatrixXi &F);
|
||||
void to_triangle_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, TriangleMesh &);
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree.
|
||||
// Implemented in libslic3r/SLA/Common.cpp
|
||||
|
@ -30,11 +33,15 @@ class EigenMesh3D {
|
|||
|
||||
public:
|
||||
|
||||
EigenMesh3D(const TriangleMesh&);
|
||||
explicit EigenMesh3D(const TriangleMesh&);
|
||||
explicit EigenMesh3D(const Contour3D &other);
|
||||
|
||||
EigenMesh3D(const EigenMesh3D& other);
|
||||
EigenMesh3D(const Contour3D &other);
|
||||
EigenMesh3D& operator=(const EigenMesh3D&);
|
||||
|
||||
EigenMesh3D(EigenMesh3D &&other);
|
||||
EigenMesh3D& operator=(EigenMesh3D &&other);
|
||||
|
||||
~EigenMesh3D();
|
||||
|
||||
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
|
||||
|
@ -70,9 +77,6 @@ public:
|
|||
inline bool is_valid() const { return m_mesh != nullptr; }
|
||||
inline bool is_hit() const { return !std::isinf(m_t); }
|
||||
|
||||
// Hit_result can decay into a double as the hit distance.
|
||||
inline operator double() const { return distance(); }
|
||||
|
||||
inline const Vec3d& normal() const {
|
||||
assert(is_valid());
|
||||
return m_normal;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <string_view>
|
||||
|
||||
#include <libslic3r/SLA/RasterWriter.hpp>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
@ -12,14 +14,16 @@
|
|||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
std::string RasterWriter::createIniContent(const std::string& projectname) const
|
||||
void RasterWriter::write_ini(const std::map<std::string, std::string> &m, std::string &ini)
|
||||
{
|
||||
for (auto ¶m : m) ini += param.first + " = " + param.second + "\n";
|
||||
}
|
||||
|
||||
std::string RasterWriter::create_ini_content(const std::string& projectname) const
|
||||
{
|
||||
std::string out("action = print\njobDir = ");
|
||||
out += projectname + "\n";
|
||||
|
||||
for (auto ¶m : m_config)
|
||||
out += param.first + " = " + param.second + "\n";
|
||||
|
||||
write_ini(m_config, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -53,7 +57,12 @@ void RasterWriter::save(Zipper &zipper, const std::string &prjname)
|
|||
|
||||
zipper.add_entry("config.ini");
|
||||
|
||||
zipper << createIniContent(project);
|
||||
zipper << create_ini_content(project);
|
||||
|
||||
zipper.add_entry("prusaslicer.ini");
|
||||
std::string prusaslicer_ini;
|
||||
write_ini(m_slicer_config, prusaslicer_ini);
|
||||
zipper << prusaslicer_ini;
|
||||
|
||||
for(unsigned i = 0; i < m_layers_rst.size(); i++)
|
||||
{
|
||||
|
@ -89,6 +98,29 @@ std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void append_full_config(const DynamicPrintConfig &cfg, std::map<std::string, std::string> &keys)
|
||||
{
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
// Sorted list of config keys, which shall not be stored into the ini.
|
||||
static constexpr auto banned_keys = {
|
||||
"compatible_printers"sv,
|
||||
"compatible_prints"sv,
|
||||
"print_host"sv,
|
||||
"printhost_apikey"sv,
|
||||
"printhost_cafile"sv
|
||||
};
|
||||
|
||||
assert(std::is_sorted(banned_keys.begin(), banned_keys.end()));
|
||||
auto is_banned = [](const std::string &key) {
|
||||
return std::binary_search(banned_keys.begin(), banned_keys.end(), key);
|
||||
};
|
||||
|
||||
for (const std::string &key : cfg.keys())
|
||||
if (! is_banned(key) && ! cfg.option(key)->is_nil())
|
||||
keys[key] = cfg.opt_serialize(key);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RasterWriter::set_config(const DynamicPrintConfig &cfg)
|
||||
|
@ -101,9 +133,9 @@ void RasterWriter::set_config(const DynamicPrintConfig &cfg)
|
|||
m_config["printerVariant"] = get_cfg_value(cfg, "printer_variant");
|
||||
m_config["printerProfile"] = get_cfg_value(cfg, "printer_settings_id");
|
||||
m_config["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id");
|
||||
|
||||
m_config["fileCreationTimestamp"] = Utils::utc_timestamp();
|
||||
m_config["prusaSlicerVersion"] = SLIC3R_BUILD_ID;
|
||||
append_full_config(cfg, m_slicer_config);
|
||||
}
|
||||
|
||||
void RasterWriter::set_statistics(const PrintStatistics &stats)
|
||||
|
|
|
@ -66,8 +66,10 @@ private:
|
|||
double m_gamma;
|
||||
|
||||
std::map<std::string, std::string> m_config;
|
||||
std::map<std::string, std::string> m_slicer_config;
|
||||
|
||||
std::string createIniContent(const std::string& projectname) const;
|
||||
static void write_ini(const std::map<std::string, std::string> &m, std::string &ini);
|
||||
std::string create_ini_content(const std::string& projectname) const;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -166,190 +166,182 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder,
|
|||
return pc == ABORT;
|
||||
}
|
||||
|
||||
// Give points on a 3D ring with given center, radius and orientation
|
||||
// method based on:
|
||||
// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space
|
||||
template<size_t N>
|
||||
class PointRing {
|
||||
std::array<double, N> m_phis;
|
||||
|
||||
// Two vectors that will be perpendicular to each other and to the
|
||||
// axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a
|
||||
// placeholder.
|
||||
// a and b vectors are perpendicular to the ring direction and to each other.
|
||||
// Together they define the plane where we have to iterate with the
|
||||
// given angles in the 'm_phis' vector
|
||||
Vec3d a = {0, 1, 0}, b;
|
||||
double m_radius = 0.;
|
||||
|
||||
static inline bool constexpr is_one(double val)
|
||||
{
|
||||
return std::abs(std::abs(val) - 1) < 1e-20;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PointRing(const Vec3d &n)
|
||||
{
|
||||
m_phis = linspace_array<N>(0., 2 * PI);
|
||||
|
||||
// We have to address the case when the direction vector v (same as
|
||||
// dir) is coincident with one of the world axes. In this case two of
|
||||
// its components will be completely zero and one is 1.0. Our method
|
||||
// becomes dangerous here due to division with zero. Instead, vector
|
||||
// 'a' can be an element-wise rotated version of 'v'
|
||||
if(is_one(n(X)) || is_one(n(Y)) || is_one(n(Z))) {
|
||||
a = {n(Z), n(X), n(Y)};
|
||||
b = {n(Y), n(Z), n(X)};
|
||||
}
|
||||
else {
|
||||
a(Z) = -(n(Y)*a(Y)) / n(Z); a.normalize();
|
||||
b = a.cross(n);
|
||||
}
|
||||
}
|
||||
|
||||
Vec3d get(size_t idx, const Vec3d src, double r) const
|
||||
{
|
||||
double phi = m_phis[idx];
|
||||
double sinphi = std::sin(phi);
|
||||
double cosphi = std::cos(phi);
|
||||
|
||||
double rpscos = r * cosphi;
|
||||
double rpssin = r * sinphi;
|
||||
|
||||
// Point on the sphere
|
||||
return {src(X) + rpscos * a(X) + rpssin * b(X),
|
||||
src(Y) + rpscos * a(Y) + rpssin * b(Y),
|
||||
src(Z) + rpscos * a(Z) + rpssin * b(Z)};
|
||||
}
|
||||
};
|
||||
|
||||
template<class C, class Hit = EigenMesh3D::hit_result>
|
||||
static Hit min_hit(const C &hits)
|
||||
{
|
||||
auto mit = std::min_element(hits.begin(), hits.end(),
|
||||
[](const Hit &h1, const Hit &h2) {
|
||||
return h1.distance() < h2.distance();
|
||||
});
|
||||
|
||||
return *mit;
|
||||
}
|
||||
|
||||
EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect(
|
||||
const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width)
|
||||
{
|
||||
static const size_t SAMPLES = 8;
|
||||
|
||||
// method based on:
|
||||
// https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space
|
||||
// Move away slightly from the touching point to avoid raycasting on the
|
||||
// inner surface of the mesh.
|
||||
|
||||
const double& sd = m_cfg.safety_distance_mm;
|
||||
|
||||
auto& m = m_mesh;
|
||||
using HitResult = EigenMesh3D::hit_result;
|
||||
|
||||
// Hit results
|
||||
std::array<HitResult, SAMPLES> hits;
|
||||
|
||||
struct Rings {
|
||||
double rpin;
|
||||
double rback;
|
||||
Vec3d spin;
|
||||
Vec3d sback;
|
||||
PointRing<SAMPLES> ring;
|
||||
|
||||
Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); }
|
||||
Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); }
|
||||
} rings {r_pin + sd, r_back + sd, s, s + width * dir, dir};
|
||||
|
||||
// We will shoot multiple rays from the head pinpoint in the direction
|
||||
// of the pinhead robe (side) surface. The result will be the smallest
|
||||
// hit distance.
|
||||
|
||||
// Move away slightly from the touching point to avoid raycasting on the
|
||||
// inner surface of the mesh.
|
||||
Vec3d v = dir; // Our direction (axis)
|
||||
Vec3d c = s + width * dir;
|
||||
const double& sd = m_cfg.safety_distance_mm;
|
||||
ccr::enumerate(hits.begin(), hits.end(),
|
||||
[&m, &rings, sd](HitResult &hit, size_t i) {
|
||||
|
||||
// Two vectors that will be perpendicular to each other and to the
|
||||
// axis. Values for a(X) and a(Y) are now arbitrary, a(Z) is just a
|
||||
// placeholder.
|
||||
Vec3d a(0, 1, 0), b;
|
||||
// Point on the circle on the pin sphere
|
||||
Vec3d ps = rings.pinring(i);
|
||||
// This is the point on the circle on the back sphere
|
||||
Vec3d p = rings.backring(i);
|
||||
|
||||
// Point ps is not on mesh but can be inside or
|
||||
// outside as well. This would cause many problems
|
||||
// with ray-casting. To detect the position we will
|
||||
// use the ray-casting result (which has an is_inside
|
||||
// predicate).
|
||||
|
||||
// The portions of the circle (the head-back circle) for which we will
|
||||
// shoot rays.
|
||||
std::array<double, SAMPLES> phis;
|
||||
for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size();
|
||||
Vec3d n = (p - ps).normalized();
|
||||
auto q = m.query_ray_hit(ps + sd * n, n);
|
||||
|
||||
auto& m = m_mesh;
|
||||
using HitResult = EigenMesh3D::hit_result;
|
||||
if (q.is_inside()) { // the hit is inside the model
|
||||
if (q.distance() > rings.rpin) {
|
||||
// If we are inside the model and the hit
|
||||
// distance is bigger than our pin circle
|
||||
// diameter, it probably indicates that the
|
||||
// support point was already inside the
|
||||
// model, or there is really no space
|
||||
// around the point. We will assign a zero
|
||||
// hit distance to these cases which will
|
||||
// enforce the function return value to be
|
||||
// an invalid ray with zero hit distance.
|
||||
// (see min_element at the end)
|
||||
hit = HitResult(0.0);
|
||||
} else {
|
||||
// re-cast the ray from the outside of the
|
||||
// object. The starting point has an offset
|
||||
// of 2*safety_distance because the
|
||||
// original ray has also had an offset
|
||||
auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n);
|
||||
hit = q2;
|
||||
}
|
||||
} else
|
||||
hit = q;
|
||||
});
|
||||
|
||||
// Hit results
|
||||
std::array<HitResult, SAMPLES> hits;
|
||||
|
||||
// We have to address the case when the direction vector v (same as
|
||||
// dir) is coincident with one of the world axes. In this case two of
|
||||
// its components will be completely zero and one is 1.0. Our method
|
||||
// becomes dangerous here due to division with zero. Instead, vector
|
||||
// 'a' can be an element-wise rotated version of 'v'
|
||||
auto chk1 = [] (double val) {
|
||||
return std::abs(std::abs(val) - 1) < 1e-20;
|
||||
};
|
||||
|
||||
if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) {
|
||||
a = {v(Z), v(X), v(Y)};
|
||||
b = {v(Y), v(Z), v(X)};
|
||||
}
|
||||
else {
|
||||
a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize();
|
||||
b = a.cross(v);
|
||||
}
|
||||
|
||||
// Now a and b vectors are perpendicular to v and to each other.
|
||||
// Together they define the plane where we have to iterate with the
|
||||
// given angles in the 'phis' vector
|
||||
ccr::enumerate(
|
||||
phis.begin(), phis.end(),
|
||||
[&hits, &m, sd, r_pin, r_back, s, a, b, c](double phi, size_t i) {
|
||||
double sinphi = std::sin(phi);
|
||||
double cosphi = std::cos(phi);
|
||||
|
||||
// Let's have a safety coefficient for the radiuses.
|
||||
double rpscos = (sd + r_pin) * cosphi;
|
||||
double rpssin = (sd + r_pin) * sinphi;
|
||||
double rpbcos = (sd + r_back) * cosphi;
|
||||
double rpbsin = (sd + r_back) * sinphi;
|
||||
|
||||
// Point on the circle on the pin sphere
|
||||
Vec3d ps(s(X) + rpscos * a(X) + rpssin * b(X),
|
||||
s(Y) + rpscos * a(Y) + rpssin * b(Y),
|
||||
s(Z) + rpscos * a(Z) + rpssin * b(Z));
|
||||
|
||||
// Point ps is not on mesh but can be inside or
|
||||
// outside as well. This would cause many problems
|
||||
// with ray-casting. To detect the position we will
|
||||
// use the ray-casting result (which has an is_inside
|
||||
// predicate).
|
||||
|
||||
// This is the point on the circle on the back sphere
|
||||
Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X),
|
||||
c(Y) + rpbcos * a(Y) + rpbsin * b(Y),
|
||||
c(Z) + rpbcos * a(Z) + rpbsin * b(Z));
|
||||
|
||||
Vec3d n = (p - ps).normalized();
|
||||
auto q = m.query_ray_hit(ps + sd * n, n);
|
||||
|
||||
if (q.is_inside()) { // the hit is inside the model
|
||||
if (q.distance() > r_pin + sd) {
|
||||
// If we are inside the model and the hit
|
||||
// distance is bigger than our pin circle
|
||||
// diameter, it probably indicates that the
|
||||
// support point was already inside the
|
||||
// model, or there is really no space
|
||||
// around the point. We will assign a zero
|
||||
// hit distance to these cases which will
|
||||
// enforce the function return value to be
|
||||
// an invalid ray with zero hit distance.
|
||||
// (see min_element at the end)
|
||||
hits[i] = HitResult(0.0);
|
||||
} else {
|
||||
// re-cast the ray from the outside of the
|
||||
// object. The starting point has an offset
|
||||
// of 2*safety_distance because the
|
||||
// original ray has also had an offset
|
||||
auto q2 = m.query_ray_hit(
|
||||
ps + (q.distance() + 2 * sd) * n, n);
|
||||
hits[i] = q2;
|
||||
}
|
||||
} else
|
||||
hits[i] = q;
|
||||
});
|
||||
|
||||
auto mit = std::min_element(hits.begin(), hits.end());
|
||||
|
||||
return *mit;
|
||||
return min_hit(hits);
|
||||
}
|
||||
|
||||
EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect(
|
||||
const Vec3d &s, const Vec3d &dir, double r, bool ins_check)
|
||||
const Vec3d &src, const Vec3d &dir, double r, bool ins_check)
|
||||
{
|
||||
static const size_t SAMPLES = 8;
|
||||
PointRing<SAMPLES> ring{dir};
|
||||
|
||||
// helper vector calculations
|
||||
Vec3d a(0, 1, 0), b;
|
||||
const double& sd = m_cfg.safety_distance_mm;
|
||||
|
||||
// INFO: for explanation of the method used here, see the previous
|
||||
// method's comments.
|
||||
|
||||
auto chk1 = [] (double val) {
|
||||
return std::abs(std::abs(val) - 1) < 1e-20;
|
||||
};
|
||||
|
||||
if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) {
|
||||
a = {dir(Z), dir(X), dir(Y)};
|
||||
b = {dir(Y), dir(Z), dir(X)};
|
||||
}
|
||||
else {
|
||||
a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize();
|
||||
b = a.cross(dir);
|
||||
}
|
||||
|
||||
// circle portions
|
||||
std::array<double, SAMPLES> phis;
|
||||
for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size();
|
||||
|
||||
auto& m = m_mesh;
|
||||
using HitResult = EigenMesh3D::hit_result;
|
||||
using Hit = EigenMesh3D::hit_result;
|
||||
|
||||
// Hit results
|
||||
std::array<HitResult, SAMPLES> hits;
|
||||
std::array<Hit, SAMPLES> hits;
|
||||
|
||||
ccr::enumerate(
|
||||
phis.begin(), phis.end(),
|
||||
[&m, a, b, sd, dir, r, s, ins_check, &hits] (double phi, size_t i) {
|
||||
double sinphi = std::sin(phi);
|
||||
double cosphi = std::cos(phi);
|
||||
|
||||
// Let's have a safety coefficient for the radiuses.
|
||||
double rcos = (sd + r) * cosphi;
|
||||
double rsin = (sd + r) * sinphi;
|
||||
|
||||
// Point on the circle on the pin sphere
|
||||
Vec3d p (s(X) + rcos * a(X) + rsin * b(X),
|
||||
s(Y) + rcos * a(Y) + rsin * b(Y),
|
||||
s(Z) + rcos * a(Z) + rsin * b(Z));
|
||||
|
||||
auto hr = m.query_ray_hit(p + sd*dir, dir);
|
||||
|
||||
if(ins_check && hr.is_inside()) {
|
||||
if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0);
|
||||
else {
|
||||
// re-cast the ray from the outside of the object
|
||||
auto hr2 =
|
||||
m.query_ray_hit(p + (hr.distance() + 2*sd)*dir, dir);
|
||||
|
||||
hits[i] = hr2;
|
||||
}
|
||||
} else hits[i] = hr;
|
||||
});
|
||||
ccr::enumerate(hits.begin(), hits.end(),
|
||||
[this, r, src, ins_check, &ring, dir] (Hit &hit, size_t i) {
|
||||
|
||||
const double sd = m_cfg.safety_distance_mm;
|
||||
|
||||
// Point on the circle on the pin sphere
|
||||
Vec3d p = ring.get(i, src, r + sd);
|
||||
|
||||
auto hr = m_mesh.query_ray_hit(p + sd * dir, dir);
|
||||
|
||||
if(ins_check && hr.is_inside()) {
|
||||
if(hr.distance() > 2 * r + sd) hit = Hit(0.0);
|
||||
else {
|
||||
// re-cast the ray from the outside of the object
|
||||
hit = m_mesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir);
|
||||
}
|
||||
} else hit = hr;
|
||||
});
|
||||
|
||||
auto mit = std::min_element(hits.begin(), hits.end());
|
||||
|
||||
return *mit;
|
||||
return min_hit(hits);
|
||||
}
|
||||
|
||||
bool SupportTreeBuildsteps::interconnect(const Pillar &pillar,
|
||||
|
@ -419,7 +411,7 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar,
|
|||
|
||||
// TODO: This is a workaround to not have a faulty last bridge
|
||||
while(ej(Z) >= eupper(Z) /*endz*/) {
|
||||
if(bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r) >= bridge_distance)
|
||||
if(bridge_mesh_distance(sj, dirv(sj, ej), pillar.r) >= bridge_distance)
|
||||
{
|
||||
m_builder.add_crossbridge(sj, ej, pillar.r);
|
||||
was_connected = true;
|
||||
|
@ -430,7 +422,7 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar,
|
|||
Vec3d sjback(ej(X), ej(Y), sj(Z));
|
||||
Vec3d ejback(sj(X), sj(Y), ej(Z));
|
||||
if (sjback(Z) <= slower(Z) && ejback(Z) >= eupper(Z) &&
|
||||
bridge_mesh_intersect(sjback, dirv(sjback, ejback),
|
||||
bridge_mesh_distance(sjback, dirv(sjback, ejback),
|
||||
pillar.r) >= bridge_distance) {
|
||||
// need to check collision for the cross stick
|
||||
m_builder.add_crossbridge(sjback, ejback, pillar.r);
|
||||
|
@ -487,7 +479,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
|
|||
bridgestart(Z) -= zdiff;
|
||||
touchjp(Z) = Zdown;
|
||||
|
||||
double t = bridge_mesh_intersect(headjp, {0,0,-1}, r);
|
||||
double t = bridge_mesh_distance(headjp, DOWN, r);
|
||||
|
||||
// We can't insert a pillar under the source head to connect
|
||||
// with the nearby pillar's starting junction
|
||||
|
@ -505,8 +497,7 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
|
|||
double minz = m_builder.ground_level + 2 * m_cfg.head_width_mm;
|
||||
if(bridgeend(Z) < minz) return false;
|
||||
|
||||
double t = bridge_mesh_intersect(bridgestart,
|
||||
dirv(bridgestart, bridgeend), r);
|
||||
double t = bridge_mesh_distance(bridgestart, dirv(bridgestart, bridgeend), r);
|
||||
|
||||
// Cannot insert the bridge. (further search might not worth the hassle)
|
||||
if(t < distance(bridgestart, bridgeend)) return false;
|
||||
|
@ -633,7 +624,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
|
|||
};
|
||||
|
||||
// We have to check if the bridge is feasible.
|
||||
if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm())
|
||||
if (bridge_mesh_distance(jp, dir, radius) < (endp - jp).norm())
|
||||
abort_in_shame();
|
||||
else {
|
||||
// If the new endpoint is below ground, do not make a pillar
|
||||
|
@ -764,7 +755,7 @@ void SupportTreeBuildsteps::filter()
|
|||
{
|
||||
auto dir = spheric_to_dir(plr, azm).normalized();
|
||||
|
||||
double score = pinhead_mesh_intersect(
|
||||
double score = pinhead_mesh_distance(
|
||||
hp, dir, pin_r, m_cfg.head_back_radius_mm, w);
|
||||
|
||||
return score;
|
||||
|
@ -950,11 +941,11 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir)
|
|||
{
|
||||
auto hjp = head.junction_point();
|
||||
double r = head.r_back_mm;
|
||||
double t = bridge_mesh_intersect(hjp, dir, head.r_back_mm);
|
||||
double t = bridge_mesh_distance(hjp, dir, head.r_back_mm);
|
||||
double d = 0, tdown = 0;
|
||||
t = std::min(t, m_cfg.max_bridge_length_mm);
|
||||
|
||||
while (d < t && !std::isinf(tdown = bridge_mesh_intersect(hjp + d * dir, DOWN, r)))
|
||||
while (d < t && !std::isinf(tdown = bridge_mesh_distance(hjp + d * dir, DOWN, r)))
|
||||
d += r;
|
||||
|
||||
if(!std::isinf(tdown)) return false;
|
||||
|
@ -989,7 +980,7 @@ bool SupportTreeBuildsteps::connect_to_ground(Head &head)
|
|||
auto oresult = solver.optimize_max(
|
||||
[this, hjp, r_back](double plr, double azm) {
|
||||
Vec3d n = spheric_to_dir(plr, azm).normalized();
|
||||
return bridge_mesh_intersect(hjp, n, r_back);
|
||||
return bridge_mesh_distance(hjp, n, r_back);
|
||||
},
|
||||
initvals(polar, azimuth), // let's start with what we have
|
||||
bound(3*PI/4, PI), // Must not exceed the slope limit
|
||||
|
@ -1259,9 +1250,8 @@ void SupportTreeBuildsteps::interconnect_pillars()
|
|||
m_pillar_index.insert(pp.endpoint(), unsigned(pp.id));
|
||||
|
||||
m_builder.add_junction(s, pillar().r);
|
||||
double t = bridge_mesh_intersect(pillarsp,
|
||||
dirv(pillarsp, s),
|
||||
pillar().r);
|
||||
double t = bridge_mesh_distance(pillarsp, dirv(pillarsp, s),
|
||||
pillar().r);
|
||||
if (distance(pillarsp, s) < t)
|
||||
m_builder.add_bridge(pillarsp, s, pillar().r);
|
||||
|
||||
|
@ -1312,8 +1302,8 @@ void SupportTreeBuildsteps::routing_headless()
|
|||
Vec3d sj = sp + R * n; // stick start point
|
||||
|
||||
// This is only for checking
|
||||
double idist = bridge_mesh_intersect(sph, DOWN, R, true);
|
||||
double realdist = ray_mesh_intersect(sj, DOWN);
|
||||
double idist = bridge_mesh_distance(sph, DOWN, R, true);
|
||||
double realdist = ray_mesh_intersect(sj, DOWN).distance();
|
||||
double dist = realdist;
|
||||
|
||||
if (std::isinf(dist)) dist = sph(Z) - m_builder.ground_level;
|
||||
|
|
|
@ -206,10 +206,10 @@ class SupportTreeBuildsteps {
|
|||
// When bridging heads to pillars... TODO: find a cleaner solution
|
||||
ccr::BlockingMutex m_bridge_mutex;
|
||||
|
||||
inline double ray_mesh_intersect(const Vec3d& s,
|
||||
const Vec3d& dir)
|
||||
inline EigenMesh3D::hit_result ray_mesh_intersect(const Vec3d& s,
|
||||
const Vec3d& dir)
|
||||
{
|
||||
return m_mesh.query_ray_hit(s, dir).distance();
|
||||
return m_mesh.query_ray_hit(s, dir);
|
||||
}
|
||||
|
||||
// This function will test if a future pinhead would not collide with the
|
||||
|
@ -229,6 +229,11 @@ class SupportTreeBuildsteps {
|
|||
double r_pin,
|
||||
double r_back,
|
||||
double width);
|
||||
|
||||
template<class...Args>
|
||||
inline double pinhead_mesh_distance(Args&&...args) {
|
||||
return pinhead_mesh_intersect(std::forward<Args>(args)...).distance();
|
||||
}
|
||||
|
||||
// Checking bridge (pillar and stick as well) intersection with the model.
|
||||
// If the function is used for headless sticks, the ins_check parameter
|
||||
|
@ -243,6 +248,11 @@ class SupportTreeBuildsteps {
|
|||
const Vec3d& dir,
|
||||
double r,
|
||||
bool ins_check = false);
|
||||
|
||||
template<class...Args>
|
||||
inline double bridge_mesh_distance(Args&&...args) {
|
||||
return bridge_mesh_intersect(std::forward<Args>(args)...).distance();
|
||||
}
|
||||
|
||||
// Helper function for interconnecting two pillars with zig-zag bridges.
|
||||
bool interconnect(const Pillar& pillar, const Pillar& nextpillar);
|
||||
|
|
|
@ -678,7 +678,7 @@ void SLAPrint::process()
|
|||
|
||||
// We want to first process all objects...
|
||||
std::vector<SLAPrintObjectStep> level1_obj_steps = {
|
||||
slaposHollowing, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad
|
||||
slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad
|
||||
};
|
||||
|
||||
// and then slice all supports to allow preview to be displayed ASAP
|
||||
|
@ -984,10 +984,10 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
|||
// propagate to dependent steps
|
||||
if (step == slaposHollowing) {
|
||||
invalidated |= this->invalidate_all_steps();
|
||||
} else if (step == slaposObjectSlice) {
|
||||
invalidated |= this->invalidate_steps({ slaposDrillHolesIfHollowed, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
||||
} else if (step == slaposDrillHoles) {
|
||||
invalidated |= this->invalidate_steps({ slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
} else if (step == slaposDrillHolesIfHollowed) {
|
||||
} else if (step == slaposObjectSlice) {
|
||||
invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
} else if (step == slaposSupportPoints) {
|
||||
|
@ -1095,8 +1095,6 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
|
|||
const std::vector<ExPolygons>& v = o == soModel? m_po->get_model_slices() :
|
||||
m_po->get_support_slices();
|
||||
|
||||
if(idx >= v.size()) return EMPTY_SLICE;
|
||||
|
||||
return idx >= v.size() ? EMPTY_SLICE : v[idx];
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ enum SLAPrintStep : unsigned int {
|
|||
|
||||
enum SLAPrintObjectStep : unsigned int {
|
||||
slaposHollowing,
|
||||
slaposDrillHoles,
|
||||
slaposObjectSlice,
|
||||
slaposDrillHolesIfHollowed,
|
||||
slaposSupportPoints,
|
||||
slaposSupportTree,
|
||||
slaposPad,
|
||||
|
@ -138,9 +138,9 @@ public:
|
|||
// Returns the current layer height
|
||||
float layer_height() const { return m_height; }
|
||||
|
||||
bool is_valid() const { return ! std::isnan(m_slice_z); }
|
||||
bool is_valid() const { return m_po && ! std::isnan(m_slice_z); }
|
||||
|
||||
const SLAPrintObject* print_obj() const { assert(m_po); return m_po; }
|
||||
const SLAPrintObject* print_obj() const { return m_po; }
|
||||
|
||||
// Methods for setting the indices into the slice vectors.
|
||||
void set_model_slice_idx(const SLAPrintObject &po, size_t id) {
|
||||
|
|
|
@ -26,9 +26,9 @@ namespace Slic3r {
|
|||
namespace {
|
||||
|
||||
const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = {
|
||||
5, // slaposHollowing,
|
||||
20, // slaposObjectSlice,
|
||||
5, // slaposDrillHolesIfHollowed
|
||||
10, // slaposHollowing,
|
||||
10, // slaposDrillHolesIfHollowed
|
||||
10, // slaposObjectSlice,
|
||||
20, // slaposSupportPoints,
|
||||
10, // slaposSupportTree,
|
||||
10, // slaposPad,
|
||||
|
@ -38,9 +38,9 @@ const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = {
|
|||
std::string OBJ_STEP_LABELS(size_t idx)
|
||||
{
|
||||
switch (idx) {
|
||||
case slaposHollowing: return L("Hollowing and drilling holes");
|
||||
case slaposHollowing: return L("Hollowing model");
|
||||
case slaposDrillHoles: return L("Drilling holes into hollowed model.");
|
||||
case slaposObjectSlice: return L("Slicing model");
|
||||
case slaposDrillHolesIfHollowed: return L("Drilling holes into hollowed model.");
|
||||
case slaposSupportPoints: return L("Generating support points");
|
||||
case slaposSupportTree: return L("Generating support tree");
|
||||
case slaposPad: return L("Generating pad");
|
||||
|
@ -80,70 +80,70 @@ SLAPrint::Steps::Steps(SLAPrint *print)
|
|||
void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
||||
{
|
||||
po.m_hollowing_data.reset();
|
||||
bool drilling_needed = ! po.m_model_object->sla_drain_holes.empty();
|
||||
|
||||
// If the mesh is broken, stop immediately, even before hollowing.
|
||||
//if (drilling_needed && po.transformed_mesh().needed_repair())
|
||||
// throw std::runtime_error(L("The mesh appears to be too broken "
|
||||
// "to drill holes into it reliably."));
|
||||
|
||||
if (! po.m_config.hollowing_enable.getBool())
|
||||
BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!";
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!";
|
||||
|
||||
double thickness = po.m_config.hollowing_min_thickness.getFloat();
|
||||
double quality = po.m_config.hollowing_quality.getFloat();
|
||||
double closing_d = po.m_config.hollowing_closing_distance.getFloat();
|
||||
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
|
||||
auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg);
|
||||
|
||||
if (meshptr->empty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
|
||||
else {
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
po.m_hollowing_data->interior = *meshptr;
|
||||
auto &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||
hollowed_mesh = po.transformed_mesh();
|
||||
hollowed_mesh.merge(po.m_hollowing_data->interior);
|
||||
hollowed_mesh.require_shared_vertices();
|
||||
}
|
||||
if (! po.m_config.hollowing_enable.getBool()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!";
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!";
|
||||
|
||||
// Drill holes into the hollowed/original mesh.
|
||||
if (! drilling_needed)
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
|
||||
double thickness = po.m_config.hollowing_min_thickness.getFloat();
|
||||
double quality = po.m_config.hollowing_quality.getFloat();
|
||||
double closing_d = po.m_config.hollowing_closing_distance.getFloat();
|
||||
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
|
||||
auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg);
|
||||
|
||||
if (meshptr->empty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
|
||||
sla::DrainHoles drainholes = po.transformed_drainhole_points();
|
||||
|
||||
TriangleMesh holes_mesh;
|
||||
|
||||
for (const sla::DrainHole &holept : drainholes)
|
||||
holes_mesh.merge(sla::to_triangle_mesh(holept.to_mesh()));
|
||||
|
||||
holes_mesh.require_shared_vertices();
|
||||
MeshBoolean::self_union(holes_mesh);
|
||||
|
||||
// If there is no hollowed mesh yet, copy the original mesh.
|
||||
if (! po.m_hollowing_data) {
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
po.m_hollowing_data->hollow_mesh_with_holes = po.transformed_mesh();
|
||||
}
|
||||
|
||||
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||
hollowed_mesh = po.get_mesh_to_print();
|
||||
try {
|
||||
MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh);
|
||||
}
|
||||
catch (const std::runtime_error& ex) {
|
||||
throw std::runtime_error(L("Drilling holes into the mesh failed. "
|
||||
"This is usually caused by broken model. Try to fix it first."));
|
||||
}
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
po.m_hollowing_data->interior = *meshptr;
|
||||
auto &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||
hollowed_mesh = po.transformed_mesh();
|
||||
hollowed_mesh.merge(po.m_hollowing_data->interior);
|
||||
hollowed_mesh.require_shared_vertices();
|
||||
}
|
||||
}
|
||||
|
||||
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
{
|
||||
// Drill holes into the hollowed/original mesh.
|
||||
if (po.m_model_object->sla_drain_holes.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
|
||||
sla::DrainHoles drainholes = po.transformed_drainhole_points();
|
||||
|
||||
TriangleMesh holes_mesh;
|
||||
|
||||
for (const sla::DrainHole &holept : drainholes)
|
||||
holes_mesh.merge(sla::to_triangle_mesh(holept.to_mesh()));
|
||||
|
||||
holes_mesh.require_shared_vertices();
|
||||
MeshBoolean::cgal::self_union(holes_mesh); //FIXME-fix and use the cgal version
|
||||
|
||||
// If there is no hollowed mesh yet, copy the original mesh.
|
||||
if (! po.m_hollowing_data) {
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
po.m_hollowing_data->hollow_mesh_with_holes = po.transformed_mesh();
|
||||
}
|
||||
|
||||
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||
|
||||
try {
|
||||
MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh);
|
||||
} catch (const std::runtime_error &ex) {
|
||||
throw std::runtime_error(L(
|
||||
"Drilling holes into the mesh failed. "
|
||||
"This is usually caused by broken model. Try to fix it first."));
|
||||
}
|
||||
|
||||
hollowed_mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
// The slicing will be performed on an imaginary 1D grid which starts from
|
||||
// the bottom of the bounding box created around the supported model. So
|
||||
// the first layer which is usually thicker will be part of the supports
|
||||
|
@ -478,14 +478,16 @@ static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPo
|
|||
}
|
||||
|
||||
// get polygons for all instances in the object
|
||||
static ClipperPolygons get_all_polygons(
|
||||
const ExPolygons & input_polygons,
|
||||
const std::vector<SLAPrintObject::Instance> &instances,
|
||||
bool is_lefthanded)
|
||||
static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
|
||||
{
|
||||
namespace sl = libnest2d::sl;
|
||||
|
||||
if (!record.print_obj()) return {};
|
||||
|
||||
ClipperPolygons polygons;
|
||||
auto &input_polygons = record.get_slice(o);
|
||||
auto &instances = record.print_obj()->instances();
|
||||
bool is_lefthanded = record.print_obj()->is_left_handed();
|
||||
polygons.reserve(input_polygons.size() * instances.size());
|
||||
|
||||
for (const ExPolygon& polygon : input_polygons) {
|
||||
|
@ -558,6 +560,12 @@ void SLAPrint::Steps::initialize_printer_input()
|
|||
coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs;
|
||||
|
||||
for(const SliceRecord& slicerecord : o->get_slice_index()) {
|
||||
if (!slicerecord.is_valid())
|
||||
throw std::runtime_error(
|
||||
L("There are unprintable objects. Try to "
|
||||
"adjust support settings to make the "
|
||||
"objects printable."));
|
||||
|
||||
coord_t lvlid = slicerecord.print_level() - gndlvl;
|
||||
|
||||
// Neat trick to round the layer levels to the grid.
|
||||
|
@ -660,22 +668,13 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
supports_polygons.reserve(c);
|
||||
|
||||
for(const SliceRecord& record : layer.slices()) {
|
||||
const SLAPrintObject *po = record.print_obj();
|
||||
|
||||
const ExPolygons &modelslices = record.get_slice(soModel);
|
||||
|
||||
bool is_lefth = record.print_obj()->is_left_handed();
|
||||
if (!modelslices.empty()) {
|
||||
ClipperPolygons v = get_all_polygons(modelslices, po->instances(), is_lefth);
|
||||
for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp));
|
||||
}
|
||||
|
||||
const ExPolygons &supportslices = record.get_slice(soSupport);
|
||||
|
||||
if (!supportslices.empty()) {
|
||||
ClipperPolygons v = get_all_polygons(supportslices, po->instances(), is_lefth);
|
||||
for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp));
|
||||
}
|
||||
ClipperPolygons modelslices = get_all_polygons(record, soModel);
|
||||
for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
|
||||
|
||||
ClipperPolygons supportslices = get_all_polygons(record, soSupport);
|
||||
for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
|
||||
|
||||
}
|
||||
|
||||
model_polygons = polyunion(model_polygons);
|
||||
|
@ -864,8 +863,8 @@ void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj)
|
|||
{
|
||||
switch(step) {
|
||||
case slaposHollowing: hollow_model(obj); break;
|
||||
case slaposDrillHoles: drill_holes(obj); break;
|
||||
case slaposObjectSlice: slice_model(obj); break;
|
||||
case slaposDrillHolesIfHollowed: break;
|
||||
case slaposSupportPoints: support_points(obj); break;
|
||||
case slaposSupportTree: support_tree(obj); break;
|
||||
case slaposPad: generate_pad(obj); break;
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
Steps(SLAPrint *print);
|
||||
|
||||
void hollow_model(SLAPrintObject &po);
|
||||
void drill_holes (SLAPrintObject &po);
|
||||
void slice_model(SLAPrintObject& po);
|
||||
void support_points(SLAPrintObject& po);
|
||||
void support_tree(SLAPrintObject& po);
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
|
@ -63,7 +65,7 @@ namespace implementation {
|
|||
template<bool B, class T>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
// Meta predicates for floating, 'scaled coord' and generic arithmetic types
|
||||
// Meta predicates for floating, integer and generic arithmetic types
|
||||
template<class T, class O = T>
|
||||
using FloatingOnly = enable_if_t<std::is_floating_point<T>::value, O>;
|
||||
|
||||
|
@ -82,41 +84,15 @@ struct remove_cvref {
|
|||
template< class T >
|
||||
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
|
||||
struct DOut {
|
||||
#ifndef NDEBUG
|
||||
std::ostream& out = std::cout;
|
||||
#endif
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline DOut&& operator<<( DOut&& out, T&& d) {
|
||||
#ifndef NDEBUG
|
||||
out.out << d;
|
||||
#endif
|
||||
return std::move(out);
|
||||
}
|
||||
|
||||
inline DOut dout() { return DOut(); }
|
||||
|
||||
template<class T> FloatingOnly<T, bool> is_approx(T val, T ref) { return std::abs(val - ref) < 1e-8; }
|
||||
template<class T> IntegerOnly <T, bool> is_approx(T val, T ref) { val == ref; }
|
||||
|
||||
template<class T, size_t N = 10> class SymetricMatrix {
|
||||
template<class T> class SymetricMatrix {
|
||||
static const constexpr size_t N = 10;
|
||||
public:
|
||||
|
||||
explicit SymetricMatrix(ArithmeticOnly<T> c = T()) { std::fill(m, m + N, c); }
|
||||
|
||||
SymetricMatrix(T m11, T m12, T m13, T m14,
|
||||
T m22, T m23, T m24,
|
||||
T m33, T m34,
|
||||
T m44)
|
||||
{
|
||||
m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14;
|
||||
m[4] = m22; m[5] = m23; m[6] = m24;
|
||||
m[7] = m33; m[8] = m34;
|
||||
m[9] = m44;
|
||||
}
|
||||
|
||||
// Make plane
|
||||
SymetricMatrix(T a, T b, T c, T d)
|
||||
{
|
||||
|
@ -140,21 +116,16 @@ public:
|
|||
return det;
|
||||
}
|
||||
|
||||
const SymetricMatrix operator+(const SymetricMatrix& n) const
|
||||
const SymetricMatrix& operator+=(const SymetricMatrix& n)
|
||||
{
|
||||
return SymetricMatrix(m[0] + n[0], m[1] + n[1], m[2] + n[2], m[3]+n[3],
|
||||
m[4] + n[4], m[5] + n[5], m[6] + n[6],
|
||||
m[7] + n[7], m[8] + n[8],
|
||||
m[9] + n[9]);
|
||||
for (size_t i = 0; i < N; ++i) m[i] += n[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
SymetricMatrix& operator+=(const SymetricMatrix& n)
|
||||
SymetricMatrix operator+(const SymetricMatrix& n)
|
||||
{
|
||||
m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3];
|
||||
m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7];
|
||||
m[8]+=n[8]; m[9]+=n[9];
|
||||
|
||||
return *this;
|
||||
SymetricMatrix self = *this;
|
||||
return self += n;
|
||||
}
|
||||
|
||||
T m[N];
|
||||
|
@ -349,10 +320,10 @@ public:
|
|||
|
||||
}
|
||||
|
||||
void simplify_mesh_lossless();
|
||||
template<class ProgressFn> void simplify_mesh_lossless(ProgressFn &&fn);
|
||||
void simplify_mesh_lossless() { simplify_mesh_lossless([](int){}); }
|
||||
};
|
||||
|
||||
|
||||
template<class Mesh> void SimplifiableMesh<Mesh>::compact_faces()
|
||||
{
|
||||
auto it = std::remove_if(m_faceinfo.begin(), m_faceinfo.end(),
|
||||
|
@ -604,7 +575,7 @@ bool SimplifiableMesh<Mesh>::flipped(const Vertex & p,
|
|||
}
|
||||
|
||||
template<class Mesh>
|
||||
void SimplifiableMesh<Mesh>::simplify_mesh_lossless()
|
||||
template<class Fn> void SimplifiableMesh<Mesh>::simplify_mesh_lossless(Fn &&fn)
|
||||
{
|
||||
// init
|
||||
for (FaceInfo &fi : m_faceinfo) fi.deleted = false;
|
||||
|
@ -628,7 +599,7 @@ void SimplifiableMesh<Mesh>::simplify_mesh_lossless()
|
|||
//
|
||||
double threshold = std::numeric_limits<double>::epsilon(); //1.0E-3 EPS; // Really? (tm)
|
||||
|
||||
dout() << "lossless iteration " << iteration << "\n";
|
||||
fn(iteration);
|
||||
|
||||
for (FaceInfo &fi : m_faceinfo) {
|
||||
if (fi.err[3] > threshold || fi.deleted || fi.dirty) continue;
|
||||
|
|
|
@ -93,8 +93,6 @@ namespace PerlUtils {
|
|||
extern std::string path_to_parent_path(const char *src);
|
||||
};
|
||||
|
||||
std::string string_printf(const char *format, ...);
|
||||
|
||||
// Standard "generated by Slic3r version xxx timestamp xxx" header string,
|
||||
// to be placed at the top of Slic3r generated files.
|
||||
std::string header_slic3r_generated();
|
||||
|
|
|
@ -231,16 +231,17 @@ static inline bool is_approx(Number value, Number test_value)
|
|||
}
|
||||
|
||||
template<class...Args>
|
||||
std::string string_printf(const char *const fmt, Args &&...args)
|
||||
std::string string_printf(const char *const _fmt, Args &&...args)
|
||||
{
|
||||
static const size_t INITIAL_LEN = 1024;
|
||||
std::vector<char> buffer(INITIAL_LEN, '\0');
|
||||
|
||||
int bufflen = snprintf(buffer.data(), INITIAL_LEN - 1, fmt, std::forward<Args>(args)...);
|
||||
auto fmt = std::string("%s") + _fmt;
|
||||
int bufflen = snprintf(buffer.data(), INITIAL_LEN - 1, fmt.c_str(), "", std::forward<Args>(args)...);
|
||||
|
||||
if (bufflen >= int(INITIAL_LEN)) {
|
||||
buffer.resize(size_t(bufflen) + 1);
|
||||
snprintf(buffer.data(), buffer.size(), fmt, std::forward<Args>(args)...);
|
||||
snprintf(buffer.data(), buffer.size(), fmt.c_str(), "", std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
return std::string(buffer.begin(), buffer.begin() + bufflen);
|
||||
|
|
|
@ -577,24 +577,6 @@ namespace PerlUtils {
|
|||
std::string path_to_parent_path(const char *src) { return boost::filesystem::path(src).parent_path().string(); }
|
||||
};
|
||||
|
||||
|
||||
std::string string_printf(const char *format, ...)
|
||||
{
|
||||
va_list args1;
|
||||
va_start(args1, format);
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
|
||||
size_t needed_size = ::vsnprintf(nullptr, 0, format, args1) + 1;
|
||||
va_end(args1);
|
||||
|
||||
std::string res(needed_size, '\0');
|
||||
::vsnprintf(&res.front(), res.size(), format, args2);
|
||||
va_end(args2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string header_slic3r_generated()
|
||||
{
|
||||
return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue