mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-24 15:13:58 -06:00
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_window
This commit is contained in:
commit
b6470c3390
32 changed files with 1612 additions and 994 deletions
|
@ -11,6 +11,8 @@
|
|||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include "Utils.hpp" // for next_highest_power_of_2()
|
||||
|
||||
extern "C"
|
||||
|
@ -752,6 +754,83 @@ void get_candidate_idxs(const TreeType& tree, const VectorType& v, std::vector<s
|
|||
return;
|
||||
}
|
||||
|
||||
// Predicate: need to be specialized for intersections of different geomteries
|
||||
template<class G> struct Intersecting {};
|
||||
|
||||
// Intersection predicate specialization for box-box intersections
|
||||
template<class CoordType, int NumD>
|
||||
struct Intersecting<Eigen::AlignedBox<CoordType, NumD>> {
|
||||
Eigen::AlignedBox<CoordType, NumD> box;
|
||||
|
||||
Intersecting(const Eigen::AlignedBox<CoordType, NumD> &bb): box{bb} {}
|
||||
|
||||
bool operator() (const typename Tree<NumD, CoordType>::Node &node) const
|
||||
{
|
||||
return box.intersects(node.bbox);
|
||||
}
|
||||
};
|
||||
|
||||
template<class G> auto intersecting(const G &g) { return Intersecting<G>{g}; }
|
||||
|
||||
template<class G> struct Containing {};
|
||||
|
||||
// Intersection predicate specialization for box-box intersections
|
||||
template<class CoordType, int NumD>
|
||||
struct Containing<Eigen::AlignedBox<CoordType, NumD>> {
|
||||
Eigen::AlignedBox<CoordType, NumD> box;
|
||||
|
||||
Containing(const Eigen::AlignedBox<CoordType, NumD> &bb): box{bb} {}
|
||||
|
||||
bool operator() (const typename Tree<NumD, CoordType>::Node &node) const
|
||||
{
|
||||
return box.contains(node.bbox);
|
||||
}
|
||||
};
|
||||
|
||||
template<class G> auto containing(const G &g) { return Containing<G>{g}; }
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<int Dims, typename T, typename Pred, typename Fn>
|
||||
void traverse_recurse(const Tree<Dims, T> &tree,
|
||||
size_t idx,
|
||||
Pred && pred,
|
||||
Fn && callback)
|
||||
{
|
||||
assert(tree.node(idx).is_valid());
|
||||
|
||||
if (!pred(tree.node(idx))) return;
|
||||
|
||||
if (tree.node(idx).is_leaf()) {
|
||||
callback(tree.node(idx).idx);
|
||||
} else {
|
||||
|
||||
// call this with left and right node idx:
|
||||
auto trv = [&](size_t idx) {
|
||||
traverse_recurse(tree, idx, std::forward<Pred>(pred),
|
||||
std::forward<Fn>(callback));
|
||||
};
|
||||
|
||||
// Left / right child node index.
|
||||
trv(Tree<Dims, T>::left_child_idx(idx));
|
||||
trv(Tree<Dims, T>::right_child_idx(idx));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Tree traversal with a predicate. Example usage:
|
||||
// traverse(tree, intersecting(QueryBox), [](size_t face_idx) {
|
||||
// /* ... */
|
||||
// });
|
||||
template<int Dims, typename T, typename Predicate, typename Fn>
|
||||
void traverse(const Tree<Dims, T> &tree, Predicate &&pred, Fn &&callback)
|
||||
{
|
||||
if (tree.empty()) return;
|
||||
|
||||
detail::traverse_recurse(tree, size_t(0), std::forward<Predicate>(pred),
|
||||
std::forward<Fn>(callback));
|
||||
}
|
||||
|
||||
} // namespace AABBTreeIndirect
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -141,6 +141,8 @@ add_library(libslic3r STATIC
|
|||
PerimeterGenerator.hpp
|
||||
PlaceholderParser.cpp
|
||||
PlaceholderParser.hpp
|
||||
Platform.cpp
|
||||
Platform.hpp
|
||||
Point.cpp
|
||||
Point.hpp
|
||||
Polygon.cpp
|
||||
|
|
|
@ -373,7 +373,7 @@ private:
|
|||
void print_machine_envelope(FILE *file, Print &print);
|
||||
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
// this flag triggers first layer speeds
|
||||
// On the first printing layer. This flag triggers first layer speeds.
|
||||
bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }
|
||||
|
||||
friend ObjectByExtruder& object_by_extruder(
|
||||
|
|
|
@ -100,6 +100,7 @@ typedef std::vector<LayerRegion*> LayerRegionPtrs;
|
|||
class Layer
|
||||
{
|
||||
public:
|
||||
// Sequential index of this layer in PrintObject::m_layers, offsetted by the number of raft layers.
|
||||
size_t id() const { return m_id; }
|
||||
void set_id(size_t id) { m_id = id; }
|
||||
PrintObject* object() { return m_object; }
|
||||
|
@ -115,7 +116,7 @@ public:
|
|||
|
||||
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
|
||||
// (with possibly differing extruder ID and slicing parameters) and merged.
|
||||
// For the first layer, if the ELephant foot compensation is applied, this lslice is uncompensated, therefore
|
||||
// For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore
|
||||
// it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer.
|
||||
// These lslices aka islands are chained by the shortest traverse distance and this traversal
|
||||
// order will be applied by the G-code generator to the extrusions fitting into these lslices.
|
||||
|
@ -170,7 +171,7 @@ protected:
|
|||
virtual ~Layer();
|
||||
|
||||
private:
|
||||
// sequential number of layer, 0-based
|
||||
// Sequential index of layer, 0-based, offsetted by number of raft layers.
|
||||
size_t m_id;
|
||||
PrintObject *m_object;
|
||||
LayerRegionPtrs m_regions;
|
||||
|
|
|
@ -74,6 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||
const PrintRegionConfig ®ion_config = this->region()->config();
|
||||
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
|
||||
bool spiral_vase = print_config.spiral_vase &&
|
||||
//FIXME account for raft layers.
|
||||
(this->layer()->id() >= size_t(region_config.bottom_solid_layers.value) &&
|
||||
this->layer()->print_z >= region_config.bottom_solid_min_thickness - EPSILON);
|
||||
|
||||
|
|
|
@ -110,33 +110,19 @@ struct CGALMesh { _EpicMesh m; };
|
|||
// Converions from and to CGAL mesh
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out)
|
||||
template<class _Mesh>
|
||||
void triangle_mesh_to_cgal(const std::vector<stl_vertex> & V,
|
||||
const std::vector<stl_triangle_vertex_indices> &F,
|
||||
_Mesh &out)
|
||||
{
|
||||
using Index3 = std::array<size_t, 3>;
|
||||
|
||||
if (M.empty()) return;
|
||||
|
||||
std::vector<typename _Mesh::Point> points;
|
||||
std::vector<Index3> indices;
|
||||
points.reserve(M.its.vertices.size());
|
||||
indices.reserve(M.its.indices.size());
|
||||
for (auto &v : M.its.vertices) points.emplace_back(v.x(), v.y(), v.z());
|
||||
for (auto &_f : M.its.indices) {
|
||||
auto f = _f.cast<size_t>();
|
||||
indices.emplace_back(Index3{f(0), f(1), f(2)});
|
||||
}
|
||||
if (F.empty()) return;
|
||||
|
||||
CGALProc::orient_polygon_soup(points, indices);
|
||||
CGALProc::polygon_soup_to_polygon_mesh(points, indices, out);
|
||||
|
||||
// Number the faces because 'orient_to_bound_a_volume' needs a face <--> index map
|
||||
unsigned index = 0;
|
||||
for (auto face : out.faces()) face = CGAL::SM_Face_index(index++);
|
||||
|
||||
if(CGAL::is_closed(out))
|
||||
CGALProc::orient_to_bound_a_volume(out);
|
||||
else
|
||||
throw Slic3r::RuntimeError("Mesh not watertight");
|
||||
for (auto &v : V)
|
||||
out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()});
|
||||
|
||||
using VI = typename _Mesh::Vertex_index;
|
||||
for (auto &f : F)
|
||||
out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
|
||||
}
|
||||
|
||||
inline Vec3d to_vec3d(const _EpicMesh::Point &v)
|
||||
|
@ -164,22 +150,30 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
|
|||
}
|
||||
|
||||
for (auto &face : cgalmesh.faces()) {
|
||||
auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face));
|
||||
int i = 0;
|
||||
Vec3i trface;
|
||||
for (auto v : vtc) trface(i++) = static_cast<int>(v);
|
||||
facets.emplace_back(trface);
|
||||
auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face));
|
||||
|
||||
int i = 0;
|
||||
Vec3i facet;
|
||||
for (auto v : vtc) {
|
||||
if (i > 2) { i = 0; break; }
|
||||
facet(i++) = v;
|
||||
}
|
||||
|
||||
if (i == 3)
|
||||
facets.emplace_back(facet);
|
||||
}
|
||||
|
||||
TriangleMesh out{points, facets};
|
||||
out.require_shared_vertices();
|
||||
out.repair();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M)
|
||||
std::unique_ptr<CGALMesh, CGALMeshDeleter>
|
||||
triangle_mesh_to_cgal(const std::vector<stl_vertex> &V,
|
||||
const std::vector<stl_triangle_vertex_indices> &F)
|
||||
{
|
||||
std::unique_ptr<CGALMesh, CGALMeshDeleter> out(new CGALMesh{});
|
||||
triangle_mesh_to_cgal(M, out->m);
|
||||
triangle_mesh_to_cgal(V, F, out->m);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -238,8 +232,8 @@ template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl
|
|||
{
|
||||
CGALMesh meshA;
|
||||
CGALMesh meshB;
|
||||
triangle_mesh_to_cgal(A, meshA.m);
|
||||
triangle_mesh_to_cgal(B, meshB.m);
|
||||
triangle_mesh_to_cgal(A.its.vertices, A.its.indices, meshA.m);
|
||||
triangle_mesh_to_cgal(B.its.vertices, B.its.indices, meshB.m);
|
||||
|
||||
_cgal_do(op, meshA, meshB);
|
||||
|
||||
|
@ -264,7 +258,7 @@ void intersect(TriangleMesh &A, const TriangleMesh &B)
|
|||
bool does_self_intersect(const TriangleMesh &mesh)
|
||||
{
|
||||
CGALMesh cgalm;
|
||||
triangle_mesh_to_cgal(mesh, cgalm.m);
|
||||
triangle_mesh_to_cgal(mesh.its.vertices, mesh.its.indices, cgalm.m);
|
||||
return CGALProc::does_self_intersect(cgalm.m);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,19 @@ namespace cgal {
|
|||
struct CGALMesh;
|
||||
struct CGALMeshDeleter { void operator()(CGALMesh *ptr); };
|
||||
|
||||
std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M);
|
||||
std::unique_ptr<CGALMesh, CGALMeshDeleter>
|
||||
triangle_mesh_to_cgal(const std::vector<stl_vertex> &V,
|
||||
const std::vector<stl_triangle_vertex_indices> &F);
|
||||
|
||||
inline std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const indexed_triangle_set &M)
|
||||
{
|
||||
return triangle_mesh_to_cgal(M.vertices, M.indices);
|
||||
}
|
||||
inline std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M)
|
||||
{
|
||||
return triangle_mesh_to_cgal(M.its);
|
||||
}
|
||||
|
||||
TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh);
|
||||
|
||||
// Do boolean mesh difference with CGAL bypassing igl.
|
||||
|
|
71
src/libslic3r/Platform.cpp
Normal file
71
src/libslic3r/Platform.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "Platform.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static auto s_platform = Platform::Uninitialized;
|
||||
static auto s_platform_flavor = PlatformFlavor::Uninitialized;
|
||||
|
||||
void detect_platform()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: Windows";
|
||||
s_platform = Platform::Windows;
|
||||
s_platform_flavor = PlatformFlavor::Generic;
|
||||
#elif defined(__APPLE__)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: OSX";
|
||||
s_platform = Platform::OSX;
|
||||
s_platform_flavor = PlatformFlavor::Generic;
|
||||
#elif defined(__linux__)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: Linux";
|
||||
s_platform = Platform::Linux;
|
||||
s_platform_flavor = PlatformFlavor::GenericLinux;
|
||||
// Test for Chromium.
|
||||
{
|
||||
FILE *f = ::fopen("/proc/version", "rt");
|
||||
if (f) {
|
||||
char buf[4096];
|
||||
// Read the 1st line.
|
||||
if (::fgets(buf, 4096, f)) {
|
||||
if (strstr(buf, "Chromium OS") != nullptr) {
|
||||
s_platform_flavor = PlatformFlavor::LinuxOnChromium;
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform flavor: LinuxOnChromium";
|
||||
} else if (strstr(buf, "microsoft") != nullptr || strstr(buf, "Microsoft") != nullptr) {
|
||||
if (boost::filesystem::exists("/run/WSL") && getenv("WSL_INTEROP") != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform flavor: WSL2";
|
||||
s_platform_flavor = PlatformFlavor::WSL2;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform flavor: WSL";
|
||||
s_platform_flavor = PlatformFlavor::WSL;
|
||||
}
|
||||
}
|
||||
}
|
||||
::fclose(f);
|
||||
}
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: OpenBSD";
|
||||
s_platform = Platform::BSDUnix;
|
||||
s_platform_flavor = PlatformFlavor::OpenBSD;
|
||||
#else
|
||||
// This should not happen.
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: Unknown";
|
||||
static_assert(false, "Unknown platform detected");
|
||||
s_platform = Platform::Unknown;
|
||||
s_platform_flavor = PlatformFlavor::Unknown;
|
||||
#endif
|
||||
}
|
||||
|
||||
Platform platform()
|
||||
{
|
||||
return s_platform;
|
||||
}
|
||||
|
||||
PlatformFlavor platform_flavor()
|
||||
{
|
||||
return s_platform_flavor;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
41
src/libslic3r/Platform.hpp
Normal file
41
src/libslic3r/Platform.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef SLIC3R_Platform_HPP
|
||||
#define SLIC3R_Platform_HPP
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class Platform
|
||||
{
|
||||
Uninitialized,
|
||||
Unknown,
|
||||
Windows,
|
||||
OSX,
|
||||
Linux,
|
||||
BSDUnix,
|
||||
};
|
||||
|
||||
enum class PlatformFlavor
|
||||
{
|
||||
Uninitialized,
|
||||
Unknown,
|
||||
// For Windows and OSX, until we need to be more specific.
|
||||
Generic,
|
||||
// For Platform::Linux
|
||||
GenericLinux,
|
||||
LinuxOnChromium,
|
||||
// Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel)
|
||||
WSL,
|
||||
// Microsoft's Windows on Linux, version 2 (virtual machine)
|
||||
WSL2,
|
||||
// For Platform::BSDUnix
|
||||
OpenBSD,
|
||||
};
|
||||
|
||||
// To be called on program start-up.
|
||||
void detect_platform();
|
||||
|
||||
Platform platform();
|
||||
PlatformFlavor platform_flavor();
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // SLIC3R_Platform_HPP
|
|
@ -36,17 +36,18 @@ struct DrainHole
|
|||
Vec3f normal;
|
||||
float radius;
|
||||
float height;
|
||||
bool failed = false;
|
||||
|
||||
DrainHole()
|
||||
: pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f)
|
||||
{}
|
||||
|
||||
DrainHole(Vec3f p, Vec3f n, float r, float h)
|
||||
: pos(p), normal(n), radius(r), height(h)
|
||||
DrainHole(Vec3f p, Vec3f n, float r, float h, bool fl = false)
|
||||
: pos(p), normal(n), radius(r), height(h), failed(fl)
|
||||
{}
|
||||
|
||||
DrainHole(const DrainHole& rhs) :
|
||||
DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height) {}
|
||||
DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {}
|
||||
|
||||
bool operator==(const DrainHole &sp) const;
|
||||
|
||||
|
@ -61,7 +62,7 @@ struct DrainHole
|
|||
|
||||
template<class Archive> inline void serialize(Archive &ar)
|
||||
{
|
||||
ar(pos, normal, radius, height);
|
||||
ar(pos, normal, radius, height, failed);
|
||||
}
|
||||
|
||||
static constexpr size_t steps = 32;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <libslic3r/SLA/SupportPointGenerator.hpp>
|
||||
|
||||
#include <libslic3r/ElephantFootCompensation.hpp>
|
||||
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
|
||||
|
@ -244,6 +245,8 @@ static std::vector<bool> create_exclude_mask(
|
|||
Vec3f face_normal = C.normalized();
|
||||
|
||||
for (const sla::DrainHole &dh : holes) {
|
||||
if (dh.failed) continue;
|
||||
|
||||
Vec3d dhpos = dh.pos.cast<double>();
|
||||
Vec3d dhend = dhpos + dh.normal.cast<double>() * dh.height;
|
||||
|
||||
|
@ -270,6 +273,36 @@ static std::vector<bool> create_exclude_mask(
|
|||
return exclude_mask;
|
||||
}
|
||||
|
||||
static indexed_triangle_set
|
||||
remove_unconnected_vertices(const indexed_triangle_set &its)
|
||||
{
|
||||
if (its.indices.empty()) {};
|
||||
|
||||
indexed_triangle_set M;
|
||||
|
||||
std::vector<int> vtransl(its.vertices.size(), -1);
|
||||
int vcnt = 0;
|
||||
for (auto &f : its.indices) {
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
if (vtransl[size_t(f(i))] < 0) {
|
||||
|
||||
M.vertices.emplace_back(its.vertices[size_t(f(i))]);
|
||||
vtransl[size_t(f(i))] = vcnt++;
|
||||
}
|
||||
|
||||
std::array<int, 3> new_f = {
|
||||
vtransl[size_t(f(0))],
|
||||
vtransl[size_t(f(1))],
|
||||
vtransl[size_t(f(2))]
|
||||
};
|
||||
|
||||
M.indices.emplace_back(new_f[0], new_f[1], new_f[2]);
|
||||
}
|
||||
|
||||
return M;
|
||||
}
|
||||
|
||||
// Drill holes into the hollowed/original mesh.
|
||||
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
{
|
||||
|
@ -313,16 +346,52 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
|||
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
|
||||
sla::DrainHoles drainholes = po.transformed_drainhole_points();
|
||||
|
||||
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
hollowed_mesh.its.vertices,
|
||||
hollowed_mesh.its.indices
|
||||
);
|
||||
|
||||
std::uniform_real_distribution<float> dist(0., float(EPSILON));
|
||||
auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({});
|
||||
for (sla::DrainHole holept : drainholes) {
|
||||
auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {});
|
||||
indexed_triangle_set part_to_drill = hollowed_mesh.its;
|
||||
|
||||
bool hole_fail = false;
|
||||
for (size_t i = 0; i < drainholes.size(); ++i) {
|
||||
sla::DrainHole holept = drainholes[i];
|
||||
|
||||
holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)};
|
||||
holept.normal.normalize();
|
||||
holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)};
|
||||
TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh());
|
||||
m.require_shared_vertices();
|
||||
auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
|
||||
MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m);
|
||||
|
||||
part_to_drill.indices.clear();
|
||||
auto bb = m.bounding_box();
|
||||
Eigen::AlignedBox<float, 3> ebb{bb.min.cast<float>(),
|
||||
bb.max.cast<float>()};
|
||||
|
||||
AABBTreeIndirect::traverse(
|
||||
tree,
|
||||
AABBTreeIndirect::intersecting(ebb),
|
||||
[&part_to_drill, &hollowed_mesh](size_t faceid)
|
||||
{
|
||||
part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]);
|
||||
});
|
||||
|
||||
auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(
|
||||
remove_unconnected_vertices(part_to_drill));
|
||||
|
||||
if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to drill hole";
|
||||
|
||||
hole_fail = drainholes[i].failed =
|
||||
po.model_object()->sla_drain_holes[i].failed = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
|
||||
MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole);
|
||||
}
|
||||
|
||||
if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal))
|
||||
|
@ -332,6 +401,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
|||
|
||||
try {
|
||||
MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal);
|
||||
|
||||
hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
|
||||
mesh_view = hollowed_mesh;
|
||||
|
||||
|
@ -348,6 +418,10 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
|||
"Drilling holes into the mesh failed. "
|
||||
"This is usually caused by broken model. Try to fix it first."));
|
||||
}
|
||||
|
||||
if (hole_fail)
|
||||
po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
|
||||
L("Failed to drill some holes into the model"));
|
||||
}
|
||||
|
||||
// The slicing will be performed on an imaginary 1D grid which starts from
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -48,39 +48,8 @@ public:
|
|||
class MyLayer
|
||||
{
|
||||
public:
|
||||
MyLayer() :
|
||||
layer_type(sltUnknown),
|
||||
print_z(0.),
|
||||
bottom_z(0.),
|
||||
height(0.),
|
||||
idx_object_layer_above(size_t(-1)),
|
||||
idx_object_layer_below(size_t(-1)),
|
||||
bridging(false),
|
||||
contact_polygons(nullptr),
|
||||
overhang_polygons(nullptr)
|
||||
{}
|
||||
|
||||
~MyLayer()
|
||||
{
|
||||
delete contact_polygons;
|
||||
contact_polygons = nullptr;
|
||||
delete overhang_polygons;
|
||||
overhang_polygons = nullptr;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
layer_type = sltUnknown;
|
||||
print_z = 0.;
|
||||
bottom_z = 0.;
|
||||
height = 0.;
|
||||
idx_object_layer_above = size_t(-1);
|
||||
idx_object_layer_below = size_t(-1);
|
||||
bridging = false;
|
||||
polygons.clear();
|
||||
delete contact_polygons;
|
||||
contact_polygons = nullptr;
|
||||
delete overhang_polygons;
|
||||
overhang_polygons = nullptr;
|
||||
*this = MyLayer();
|
||||
}
|
||||
|
||||
bool operator==(const MyLayer &layer2) const {
|
||||
|
@ -103,6 +72,21 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
void merge(MyLayer &&rhs) {
|
||||
// The union_() does not support move semantic yet, but maybe one day it will.
|
||||
this->polygons = union_(this->polygons, std::move(rhs.polygons));
|
||||
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
|
||||
if (! dst || dst->empty())
|
||||
dst = std::move(src);
|
||||
else if (src && ! src->empty())
|
||||
*dst = union_(*dst, std::move(*src));
|
||||
};
|
||||
merge(this->contact_polygons, rhs.contact_polygons);
|
||||
merge(this->overhang_polygons, rhs.overhang_polygons);
|
||||
merge(this->enforcer_polygons, rhs.enforcer_polygons);
|
||||
rhs.reset();
|
||||
}
|
||||
|
||||
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
|
||||
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
|
||||
coordf_t bottom_print_z() const { return print_z - height; }
|
||||
|
@ -110,29 +94,44 @@ public:
|
|||
// To sort the extremes of top / bottom interface layers.
|
||||
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
|
||||
|
||||
SupporLayerType layer_type;
|
||||
SupporLayerType layer_type { sltUnknown };
|
||||
// Z used for printing, in unscaled coordinates.
|
||||
coordf_t print_z;
|
||||
coordf_t print_z { 0 };
|
||||
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
|
||||
// otherwise bottom_z + gap + height = print_z.
|
||||
coordf_t bottom_z;
|
||||
coordf_t bottom_z { 0 };
|
||||
// Layer height in unscaled coordinates.
|
||||
coordf_t height;
|
||||
coordf_t height { 0 };
|
||||
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
|
||||
// If this is not a contact layer, it will be set to size_t(-1).
|
||||
size_t idx_object_layer_above;
|
||||
size_t idx_object_layer_above { size_t(-1) };
|
||||
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
|
||||
// If this is not a contact layer, it will be set to size_t(-1).
|
||||
size_t idx_object_layer_below;
|
||||
size_t idx_object_layer_below { size_t(-1) };
|
||||
// Use a bridging flow when printing this support layer.
|
||||
bool bridging;
|
||||
bool bridging { false };
|
||||
|
||||
// Polygons to be filled by the support pattern.
|
||||
Polygons polygons;
|
||||
// Currently for the contact layers only.
|
||||
// MyLayer owns the contact_polygons and overhang_polygons, they are freed by the destructor.
|
||||
Polygons *contact_polygons;
|
||||
Polygons *overhang_polygons;
|
||||
std::unique_ptr<Polygons> contact_polygons;
|
||||
std::unique_ptr<Polygons> overhang_polygons;
|
||||
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
|
||||
std::unique_ptr<Polygons> enforcer_polygons;
|
||||
};
|
||||
|
||||
struct SupportParams {
|
||||
Flow first_layer_flow;
|
||||
Flow support_material_flow;
|
||||
Flow support_material_interface_flow;
|
||||
Flow support_material_bottom_interface_flow;
|
||||
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
|
||||
bool can_merge_support_regions;
|
||||
|
||||
coordf_t support_layer_height_min;
|
||||
// coordf_t support_layer_height_max;
|
||||
|
||||
coordf_t gap_xy;
|
||||
};
|
||||
|
||||
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
|
||||
|
@ -159,17 +158,19 @@ public:
|
|||
void generate(PrintObject &object);
|
||||
|
||||
private:
|
||||
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
|
||||
|
||||
// Generate top contact layers supporting overhangs.
|
||||
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
||||
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
||||
MyLayersPtr top_contact_layers(const PrintObject &object, MyLayerStorage &layer_storage) const;
|
||||
MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
|
||||
|
||||
// Generate bottom contact layers supporting the top contact layers.
|
||||
// For a soluble interface material synchronize the layer heights with the object,
|
||||
// otherwise set the layer height to a bridging flow of a support interface nozzle.
|
||||
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
|
||||
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
|
||||
std::vector<Polygons> &layer_support_areas) const;
|
||||
const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
|
||||
MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
|
||||
|
||||
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
|
||||
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
|
||||
|
@ -240,18 +241,8 @@ private:
|
|||
// Pre-calculated parameters shared between the object slicer and the support generator,
|
||||
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
|
||||
SlicingParameters m_slicing_params;
|
||||
|
||||
Flow m_first_layer_flow;
|
||||
Flow m_support_material_flow;
|
||||
Flow m_support_material_interface_flow;
|
||||
Flow m_support_material_bottom_interface_flow;
|
||||
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
|
||||
bool m_can_merge_support_regions;
|
||||
|
||||
coordf_t m_support_layer_height_min;
|
||||
// coordf_t m_support_layer_height_max;
|
||||
|
||||
coordf_t m_gap_xy;
|
||||
// Various precomputed support parameters to be shared with external functions.
|
||||
SupportParams m_support_params;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstdarg>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Platform.hpp"
|
||||
#include "Time.hpp"
|
||||
|
||||
#ifdef WIN32
|
||||
|
@ -19,9 +20,14 @@
|
|||
#ifdef BSD
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
|
@ -417,6 +423,140 @@ std::error_code rename_file(const std::string &from, const std::string &to)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
// Copied from boost::filesystem, to support copying a file to a weird filesystem, which does not support changing file attributes,
|
||||
// for example ChromeOS Linux integration or FlashAIR WebDAV.
|
||||
// Copied and simplified from boost::filesystem::detail::copy_file() with option = overwrite_if_exists and with just the Linux path kept,
|
||||
// and only features supported by Linux 3.10 (on our build server with CentOS 7) are kept, namely sendfile with ranges and statx() are not supported.
|
||||
bool copy_file_linux(const boost::filesystem::path &from, const boost::filesystem::path &to, boost::system::error_code &ec)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
struct fd_wrapper
|
||||
{
|
||||
int fd { -1 };
|
||||
fd_wrapper() = default;
|
||||
explicit fd_wrapper(int fd) throw() : fd(fd) {}
|
||||
~fd_wrapper() throw() { if (fd >= 0) ::close(fd); }
|
||||
};
|
||||
|
||||
ec.clear();
|
||||
int err = 0;
|
||||
|
||||
// Note: Declare fd_wrappers here so that errno is not clobbered by close() that may be called in fd_wrapper destructors
|
||||
fd_wrapper infile, outfile;
|
||||
|
||||
while (true) {
|
||||
infile.fd = ::open(from.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (infile.fd < 0) {
|
||||
err = errno;
|
||||
if (err == EINTR)
|
||||
continue;
|
||||
fail:
|
||||
ec.assign(err, boost::system::system_category());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
struct ::stat from_stat;
|
||||
if (::fstat(infile.fd, &from_stat) != 0) {
|
||||
fail_errno:
|
||||
err = errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const mode_t from_mode = from_stat.st_mode;
|
||||
if (!S_ISREG(from_mode)) {
|
||||
err = ENOSYS;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Enable writing for the newly created files. Having write permission set is important e.g. for NFS,
|
||||
// which checks the file permission on the server, even if the client's file descriptor supports writing.
|
||||
mode_t to_mode = from_mode | S_IWUSR;
|
||||
int oflag = O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC;
|
||||
|
||||
while (true) {
|
||||
outfile.fd = ::open(to.c_str(), oflag, to_mode);
|
||||
if (outfile.fd < 0) {
|
||||
err = errno;
|
||||
if (err == EINTR)
|
||||
continue;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
struct ::stat to_stat;
|
||||
if (::fstat(outfile.fd, &to_stat) != 0)
|
||||
goto fail_errno;
|
||||
|
||||
to_mode = to_stat.st_mode;
|
||||
if (!S_ISREG(to_mode)) {
|
||||
err = ENOSYS;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (from_stat.st_dev == to_stat.st_dev && from_stat.st_ino == to_stat.st_ino) {
|
||||
err = EEXIST;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors.
|
||||
//FIXME Vojtech: This is a copy loop valid for Linux 2.6.33 and newer.
|
||||
// copy_file_data_copy_file_range() supports cross-filesystem copying since 5.3, but Vojtech did not want to polute this
|
||||
// function with that, we don't think the performance gain is worth it for the types of files we are copying,
|
||||
// and our build server based on CentOS 7 with Linux 3.10 does not support that anyways.
|
||||
{
|
||||
// sendfile will not send more than this amount of data in one call
|
||||
constexpr std::size_t max_send_size = 0x7ffff000u;
|
||||
uintmax_t offset = 0u;
|
||||
while (off_t(offset) < from_stat.st_size) {
|
||||
uintmax_t size_left = from_stat.st_size - offset;
|
||||
std::size_t size_to_copy = max_send_size;
|
||||
if (size_left < static_cast<uintmax_t>(max_send_size))
|
||||
size_to_copy = static_cast<std::size_t>(size_left);
|
||||
ssize_t sz = ::sendfile(outfile.fd, infile.fd, nullptr, size_to_copy);
|
||||
if (sz < 0) {
|
||||
err = errno;
|
||||
if (err == EINTR)
|
||||
continue;
|
||||
if (err == 0)
|
||||
break;
|
||||
goto fail; // err already contains the error code
|
||||
}
|
||||
offset += sz;
|
||||
}
|
||||
}
|
||||
|
||||
// If we created a new file with an explicitly added S_IWUSR permission,
|
||||
// we may need to update its mode bits to match the source file.
|
||||
if (to_mode != from_mode && ::fchmod(outfile.fd, from_mode) != 0) {
|
||||
if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||
// Ignore that. 9p filesystem does not allow fmod().
|
||||
BOOST_LOG_TRIVIAL(info) << "copy_file_linux() failed to fchmod() the output file \"" << to.string() << "\" to " << from_mode << ": " << ec.message() <<
|
||||
" This may be expected when writing to a 9p filesystem.";
|
||||
} else {
|
||||
// Generic linux. Write out an error to console. At least we may get some feedback.
|
||||
BOOST_LOG_TRIVIAL(error) << "copy_file_linux() failed to fchmod() the output file \"" << to.string() << "\" to " << from_mode << ": " << ec.message();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Use fsync/fdatasync followed by close to avoid dealing with the possibility of close failing with EINTR.
|
||||
// Even if close fails, including with EINTR, most operating systems (presumably, except HP-UX) will close the
|
||||
// file descriptor upon its return. This means that if an error happens later, when the OS flushes data to the
|
||||
// underlying media, this error will go unnoticed and we have no way to receive it from close. Calling fsync/fdatasync
|
||||
// ensures that all data have been written, and even if close fails for some unfathomable reason, we don't really
|
||||
// care at that point.
|
||||
err = ::fdatasync(outfile.fd);
|
||||
if (err != 0)
|
||||
goto fail_errno;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
CopyFileResult copy_file_inner(const std::string& from, const std::string& to, std::string& error_message)
|
||||
{
|
||||
const boost::filesystem::path source(from);
|
||||
|
@ -434,7 +574,13 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
|
|||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
ec.clear();
|
||||
#ifdef __linux__
|
||||
// We want to allow copying files on Linux to succeed even if changing the file attributes fails.
|
||||
// That may happen when copying on some exotic file system, for example Linux on Chrome.
|
||||
copy_file_linux(source, target, ec);
|
||||
#else // __linux__
|
||||
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
|
||||
#endif // __linux__
|
||||
if (ec) {
|
||||
error_message = ec.message();
|
||||
return FAIL_COPY_FILE;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue