Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_window
This commit is contained in:
commit
b6470c3390
@ -38,6 +38,7 @@
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
@ -603,6 +604,9 @@ bool CLI::setup(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
// Detect the operating system flavor after SLIC3R_LOGLEVEL is set.
|
||||
detect_platform();
|
||||
|
||||
#ifdef WIN32
|
||||
// Notify user if blacklisted library is already loaded (Nahimic)
|
||||
// If there are cases of no reports with blacklisted lib - this check should be performed later.
|
||||
|
@ -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.
|
||||
|
@ -1,15 +1,9 @@
|
||||
#include "Platform.hpp"
|
||||
|
||||
|
||||
// For starting another PrusaSlicer instance on OSX.
|
||||
// Fails to compile on Windows on the build server.
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static auto s_platform = Platform::Uninitialized;
|
||||
static auto s_platform_flavor = PlatformFlavor::Uninitialized;
|
||||
@ -74,5 +68,4 @@ PlatformFlavor platform_flavor()
|
||||
return s_platform_flavor;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
@ -1,8 +1,7 @@
|
||||
#ifndef SLIC3R_GUI_Utils_Platform_HPP
|
||||
#define SLIC3R_GUI_Utils_Platform_HPP
|
||||
#ifndef SLIC3R_Platform_HPP
|
||||
#define SLIC3R_Platform_HPP
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
enum class Platform
|
||||
{
|
||||
@ -37,8 +36,6 @@ void detect_platform();
|
||||
Platform platform();
|
||||
PlatformFlavor platform_flavor();
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // SLIC3R_GUI_Utils_Platform_HPP
|
||||
#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;
|
||||
|
@ -209,8 +209,6 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/Bonjour.hpp
|
||||
Utils/PresetUpdater.cpp
|
||||
Utils/PresetUpdater.hpp
|
||||
Utils/Platform.cpp
|
||||
Utils/Platform.hpp
|
||||
Utils/Process.cpp
|
||||
Utils/Process.hpp
|
||||
Utils/Profile.hpp
|
||||
|
@ -328,7 +328,12 @@ double Control::get_double_value(const SelectedSlider& selection)
|
||||
|
||||
int Control::get_tick_from_value(double value)
|
||||
{
|
||||
auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon());
|
||||
std::vector<double>::iterator it;
|
||||
if (m_is_smart_wipe_tower)
|
||||
it = std::find_if(m_values.begin(), m_values.end(),
|
||||
[value](const double & val) { return fabs(value - val) <= epsilon(); });
|
||||
else
|
||||
it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon());
|
||||
|
||||
if (it == m_values.end())
|
||||
return -1;
|
||||
@ -391,6 +396,16 @@ void Control::SetLayersTimes(const std::vector<float>& layers_times)
|
||||
m_layers_times[0] = layers_times[0];
|
||||
for (size_t i = 1; i < layers_times.size(); i++)
|
||||
m_layers_times[i] = m_layers_times[i - 1] + layers_times[i];
|
||||
|
||||
// Erase duplicates values from m_values and save it to the m_layers_values
|
||||
// They will be used for show the correct estimated time for MM print, when "No sparce layer" is enabled
|
||||
// See https://github.com/prusa3d/PrusaSlicer/issues/6232
|
||||
m_is_smart_wipe_tower = m_values.size() != m_layers_times.size();
|
||||
if (m_is_smart_wipe_tower) {
|
||||
m_layers_values = m_values;
|
||||
sort(m_layers_values.begin(), m_layers_values.end());
|
||||
m_layers_values.erase(unique(m_layers_values.begin(), m_layers_values.end()), m_layers_values.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Control::SetLayersTimes(const std::vector<double>& layers_times)
|
||||
@ -424,6 +439,22 @@ void Control::SetExtruderColors( const std::vector<std::string>& extruder_colors
|
||||
m_extruder_colors = extruder_colors;
|
||||
}
|
||||
|
||||
bool Control::IsNewPrint()
|
||||
{
|
||||
if (GUI::wxGetApp().plater()->printer_technology() == ptSLA)
|
||||
return false;
|
||||
const Print& print = GUI::wxGetApp().plater()->fff_print();
|
||||
std::string idxs;
|
||||
for (auto object : print.objects())
|
||||
idxs += std::to_string(object->id().id) + "_";
|
||||
|
||||
if (idxs == m_print_obj_idxs)
|
||||
return false;
|
||||
|
||||
m_print_obj_idxs = idxs;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
|
||||
{
|
||||
const double step = get_scroll_step();
|
||||
@ -495,6 +526,18 @@ void Control::render()
|
||||
}
|
||||
}
|
||||
|
||||
bool Control::is_wipe_tower_layer(int tick) const
|
||||
{
|
||||
if (!m_is_smart_wipe_tower || tick >= (int)m_values.size())
|
||||
return false;
|
||||
if (tick == 0 || (tick == (int)m_values.size() - 1 && m_values[tick] > m_values[tick - 1]))
|
||||
return false;
|
||||
if (m_values[tick - 1] == m_values[tick + 1] && m_values[tick] < m_values[tick + 1])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end)
|
||||
{
|
||||
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
@ -506,6 +549,11 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_
|
||||
if (tick == 0)
|
||||
return;
|
||||
|
||||
if (is_wipe_tower_layer(tick)) {
|
||||
m_rect_tick_action = wxRect();
|
||||
return;
|
||||
}
|
||||
|
||||
wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp();
|
||||
if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end())
|
||||
icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp();
|
||||
@ -654,6 +702,21 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
|
||||
if (value >= m_values.size())
|
||||
return "ErrVal";
|
||||
|
||||
// When "Print Settings -> Multiple Extruders -> No sparse layer" is enabled, then "Smart" Wipe Tower is used for wiping.
|
||||
// As a result, each layer with tool changes is splited for min 3 parts: first tool, wiping, second tool ...
|
||||
// So, vertical slider have to respect to this case.
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/6232.
|
||||
// m_values contains data for all layer's parts,
|
||||
// but m_layers_values contains just unique Z values.
|
||||
// Use this function for correct conversion slider position to number of printed layer
|
||||
auto get_layer_number = [this](int value) {
|
||||
double layer_print_z = m_values[is_wipe_tower_layer(value) ? std::max<int>(value - 1, 0) : value];
|
||||
auto it = std::lower_bound(m_layers_values.begin(), m_layers_values.end(), layer_print_z - epsilon());
|
||||
if (it == m_layers_values.end())
|
||||
return -1;
|
||||
return int(it - m_layers_values.begin());
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
if (m_draw_mode == dmSequentialGCodeView) {
|
||||
return (Slic3r::GUI::get_app_config()->get("seq_top_gcode_indices") == "1") ?
|
||||
@ -666,15 +729,21 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
else {
|
||||
if (label_type == ltEstimatedTime) {
|
||||
return (value < m_layers_times.size()) ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : "";
|
||||
if (m_is_smart_wipe_tower) {
|
||||
int layer_number = get_layer_number(value);
|
||||
return layer_number < 0 ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number]));
|
||||
}
|
||||
return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : "";
|
||||
}
|
||||
wxString str = m_values.empty() ?
|
||||
wxString::Format("%.*f", 2, m_label_koef * value) :
|
||||
wxString::Format("%.*f", 2, m_values[value]);
|
||||
if (label_type == ltHeight)
|
||||
return str;
|
||||
if (label_type == ltHeightWithLayer)
|
||||
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1);
|
||||
if (label_type == ltHeightWithLayer) {
|
||||
size_t layer_number = m_is_smart_wipe_tower ? (size_t)get_layer_number(value) : (m_values.empty() ? value : value + 1);
|
||||
return format_wxstr("%1%\n(%2%)", str, layer_number);
|
||||
}
|
||||
}
|
||||
|
||||
return wxEmptyString;
|
||||
@ -709,10 +778,17 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType l
|
||||
text_pos = wxPoint(std::max(2, pos.x - text_width - 1 - m_thumb_size.x), pos.y - 0.5 * text_height + 1);
|
||||
}
|
||||
|
||||
wxColour old_clr = dc.GetTextForeground();
|
||||
const wxPen& pen = is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? DARK_ORANGE_PEN : wxPen(old_clr);
|
||||
dc.SetPen(pen);
|
||||
dc.SetTextForeground(pen.GetColour());
|
||||
|
||||
if (label_type == ltEstimatedTime)
|
||||
dc.DrawLabel(label, wxRect(text_pos, wxSize(text_width, text_height)), wxALIGN_RIGHT);
|
||||
else
|
||||
dc.DrawText(label, text_pos);
|
||||
|
||||
dc.SetTextForeground(old_clr);
|
||||
}
|
||||
|
||||
void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
|
||||
@ -1275,6 +1351,8 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
||||
if (m_focus == fiColorBand)
|
||||
return m_mode != SingleExtruder ? "" :
|
||||
_L("Edit current color - Right click the colored slider segment");
|
||||
if (m_focus == fiSmartWipeTower)
|
||||
return _L("This is wipe tower layer");
|
||||
if (m_draw_mode == dmSlaPrint)
|
||||
return ""; // no drawn ticks and no tooltips for them in SlaPrinting mode
|
||||
|
||||
@ -1408,8 +1486,14 @@ void Control::OnMotion(wxMouseEvent& event)
|
||||
else if (is_point_in_rect(pos, m_rect_higher_thumb))
|
||||
m_focus = fiHigherThumb;
|
||||
else {
|
||||
m_focus = fiTick;
|
||||
tick = get_tick_near_point(pos);
|
||||
if (tick < 0 && m_is_smart_wipe_tower) {
|
||||
tick = get_value_from_position(pos);
|
||||
m_focus = tick > 0 && is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ?
|
||||
fiSmartWipeTower : fiTick;
|
||||
}
|
||||
else
|
||||
m_focus = fiTick;
|
||||
}
|
||||
m_moving_pos = pos;
|
||||
}
|
||||
@ -1938,6 +2022,9 @@ void Control::auto_color_change()
|
||||
Layer* layer = object->get_layer(i);
|
||||
double cur_area = area(layer->lslices);
|
||||
|
||||
if (cur_area > prev_area)
|
||||
break;
|
||||
|
||||
if (prev_area - cur_area > delta_area) {
|
||||
int tick = get_tick_from_value(layer->print_z);
|
||||
if (tick >= 0 && !m_ticks.has_tick(tick)) {
|
||||
@ -1951,6 +2038,10 @@ void Control::auto_color_change()
|
||||
extruder = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// allow max 3 auto color changes
|
||||
if (m_ticks.ticks.size() == 3)
|
||||
break;
|
||||
}
|
||||
|
||||
prev_area = cur_area;
|
||||
|
@ -43,6 +43,7 @@ enum FocusedItem {
|
||||
fiActionIcon,
|
||||
fiLowerThumb,
|
||||
fiHigherThumb,
|
||||
fiSmartWipeTower,
|
||||
fiTick
|
||||
};
|
||||
|
||||
@ -233,6 +234,8 @@ public:
|
||||
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
|
||||
void SetExtruderColors(const std::vector<std::string>& extruder_colors);
|
||||
|
||||
bool IsNewPrint();
|
||||
|
||||
void set_render_as_disabled(bool value) { m_render_as_disabled = value; }
|
||||
bool is_rendering_as_disabled() const { return m_render_as_disabled; }
|
||||
|
||||
@ -303,6 +306,7 @@ protected:
|
||||
void correct_higher_value();
|
||||
void move_current_thumb(const bool condition);
|
||||
void enter_window(wxMouseEvent& event, const bool enter);
|
||||
bool is_wipe_tower_layer(int tick) const;
|
||||
|
||||
private:
|
||||
|
||||
@ -366,6 +370,7 @@ private:
|
||||
bool m_is_focused = false;
|
||||
bool m_force_mode_apply = true;
|
||||
bool m_enable_action_icon = true;
|
||||
bool m_is_smart_wipe_tower = false; //This flag indicates that for current print is used "smart" wipe tower (Print Settings->Multiple Extruders->No sparse layer is enabled)
|
||||
|
||||
DrawMode m_draw_mode = dmRegular;
|
||||
|
||||
@ -394,7 +399,9 @@ private:
|
||||
std::vector<double> m_values;
|
||||
TickCodeInfo m_ticks;
|
||||
std::vector<double> m_layers_times;
|
||||
std::vector<double> m_layers_values;
|
||||
std::vector<std::string> m_extruder_colors;
|
||||
std::string m_print_obj_idxs;
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
std::vector<double> m_alternate_values;
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/Utils/Platform.hpp"
|
||||
|
||||
// To show a message box if GUI initialization ends up with an exception thrown.
|
||||
#include <wx/msgdlg.h>
|
||||
@ -37,8 +36,6 @@ int GUI_Run(GUI_InitParams ¶ms)
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
#endif // __APPLE__
|
||||
|
||||
detect_platform();
|
||||
|
||||
try {
|
||||
GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
|
||||
if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
//#include "stdlib.h"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
@ -642,26 +643,26 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes.front().layers_times);
|
||||
|
||||
// Suggest the auto color change, if model looks like sign
|
||||
if (ticks_info_from_model.gcodes.empty())
|
||||
if (m_layers_slider->IsNewPrint())
|
||||
{
|
||||
NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||
notif_mngr->close_notification_of_type(NotificationType::SignDetected);
|
||||
|
||||
const Print& print = wxGetApp().plater()->fff_print();
|
||||
double delta_area = scale_(scale_(25)); // equal to 25 mm2
|
||||
|
||||
//bool is_possible_auto_color_change = false;
|
||||
for (auto object : print.objects()) {
|
||||
// bottom layer have to be a biggest, so control relation between bottom lazer and object size
|
||||
const ExPolygons& bottom = object->get_layer(0)->lslices;
|
||||
double bottom_area = area(bottom);
|
||||
if (bottom_area < double(object->size().x()) * double(object->size().y()))
|
||||
continue;
|
||||
|
||||
// if it's sign, than object have not to be a too height
|
||||
double height = object->height();
|
||||
coord_t longer_side = std::max(object->size().x(), object->size().y());
|
||||
if (height / longer_side > 0.3)
|
||||
continue;
|
||||
|
||||
const ExPolygons& bottom = object->get_layer(0)->lslices;
|
||||
if (bottom.size() > 1 || !bottom[0].holes.empty())
|
||||
continue;
|
||||
|
||||
double bottom_area = area(bottom);
|
||||
// at least 30% of object's height have to be a solid
|
||||
int i;
|
||||
for (i = 1; i < int(0.3 * object->layers().size()); i++)
|
||||
if (area(object->get_layer(1)->lslices) != bottom_area)
|
||||
@ -671,17 +672,17 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
|
||||
double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices);
|
||||
if( bottom_area - top_area > delta_area) {
|
||||
NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||
notif_mngr->push_notification(
|
||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification,
|
||||
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
|
||||
_u8L("Apply auto color change to print"),
|
||||
[this, notif_mngr](wxEvtHandler*) {
|
||||
notif_mngr->close_notification_of_type(NotificationType::SignDetected);
|
||||
[this](wxEvtHandler*) {
|
||||
m_layers_slider->auto_color_change();
|
||||
return true;
|
||||
});
|
||||
|
||||
notif_mngr->set_in_preview(true);
|
||||
notif_mngr->apply_in_preview();
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -122,9 +122,10 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
|
||||
glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift()));
|
||||
glsafe(::glMultMatrixd(instance_matrix.data()));
|
||||
|
||||
float render_color[4];
|
||||
std::array<float, 4> render_color;
|
||||
const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
|
||||
size_t cache_size = drain_holes.size();
|
||||
|
||||
for (size_t i = 0; i < cache_size; ++i)
|
||||
{
|
||||
const sla::DrainHole& drain_hole = drain_holes[i];
|
||||
@ -136,26 +137,27 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
|
||||
// First decide about the color of the point.
|
||||
if (picking) {
|
||||
std::array<float, 4> color = picking_color_component(i);
|
||||
render_color[0] = color[0];
|
||||
render_color[1] = color[1];
|
||||
render_color[2] = color[2];
|
||||
render_color[3] = color[3];
|
||||
|
||||
render_color = color;
|
||||
}
|
||||
else {
|
||||
render_color[3] = 1.f;
|
||||
if (size_t(m_hover_id) == i) {
|
||||
render_color[0] = 0.f;
|
||||
render_color[1] = 1.0f;
|
||||
render_color[2] = 1.0f;
|
||||
render_color = {0.f, 1.f, 1.f, 1.f};
|
||||
} else if (m_c->hollowed_mesh() &&
|
||||
i < m_c->hollowed_mesh()->get_drainholes().size() &&
|
||||
m_c->hollowed_mesh()->get_drainholes()[i].failed) {
|
||||
render_color = {1.f, 0.f, 0.f, .5f};
|
||||
}
|
||||
else { // neigher hover nor picking
|
||||
|
||||
render_color[0] = point_selected ? 1.0f : 0.7f;
|
||||
render_color[1] = point_selected ? 0.3f : 0.7f;
|
||||
render_color[2] = point_selected ? 0.3f : 0.7f;
|
||||
render_color[3] = 0.5f;
|
||||
}
|
||||
}
|
||||
glsafe(::glColor4fv(render_color));
|
||||
|
||||
glsafe(::glColor4fv(render_color.data()));
|
||||
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
|
||||
glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive));
|
||||
|
||||
|
@ -205,6 +205,7 @@ void HollowedMesh::on_update()
|
||||
m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
|
||||
Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse();
|
||||
m_hollowed_mesh_transformed->transform(trafo_inv);
|
||||
m_drainholes = print_object->model_object()->sla_drain_holes;
|
||||
m_old_hollowing_timestamp = timestamp;
|
||||
|
||||
const TriangleMesh &interior = print_object->hollowed_interior_mesh();
|
||||
@ -215,8 +216,9 @@ void HollowedMesh::on_update()
|
||||
m_hollowed_interior_transformed->transform(trafo_inv);
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
m_hollowed_mesh_transformed.reset(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <map>
|
||||
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
#include "libslic3r/SLA/Hollowing.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -198,6 +199,8 @@ public:
|
||||
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
|
||||
#endif // NDEBUG
|
||||
|
||||
const sla::DrainHoles &get_drainholes() const { return m_drainholes; }
|
||||
|
||||
const TriangleMesh* get_hollowed_mesh() const;
|
||||
const TriangleMesh* get_hollowed_interior() const;
|
||||
|
||||
@ -211,6 +214,7 @@ private:
|
||||
size_t m_old_hollowing_timestamp = 0;
|
||||
int m_print_object_idx = -1;
|
||||
int m_print_objects_count = 0;
|
||||
sla::DrainHoles m_drainholes;
|
||||
};
|
||||
|
||||
|
||||
|
@ -672,7 +672,7 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab)
|
||||
|
||||
bool MainFrame::can_start_new_project() const
|
||||
{
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
|
||||
}
|
||||
|
||||
bool MainFrame::can_save() const
|
||||
@ -1217,9 +1217,14 @@ void MainFrame::init_menubar_as_editor()
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"),
|
||||
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
|
||||
[]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
|
||||
#ifndef __APPLE__
|
||||
// OSX adds its own menu item to toggle fullscreen.
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"),
|
||||
[this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen()); }, this,
|
||||
[]() { return true; }, [this]() { return this->IsFullScreen(); }, this);
|
||||
[this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(),
|
||||
// wxFULLSCREEN_ALL: wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION
|
||||
wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); },
|
||||
this, []() { return true; }, [this]() { return this->IsFullScreen(); }, this);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
// Help menu
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "ImGuiWrapper.hpp"
|
||||
#include "PrintHostDialogs.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
@ -827,8 +828,6 @@ void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgu
|
||||
//------PrintHostUploadNotification----------------
|
||||
void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
|
||||
{
|
||||
if (m_uj_state == UploadJobState::PB_CANCELLED)
|
||||
return;
|
||||
m_percentage = percent;
|
||||
if (percent >= 1.0f) {
|
||||
m_uj_state = UploadJobState::PB_COMPLETED;
|
||||
@ -914,11 +913,7 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr) {
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id);
|
||||
wxQueueEvent(m_evt_handler, evt);
|
||||
}
|
||||
wxGetApp().printhost_job_queue().cancel(m_job_id - 1);
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
@ -926,11 +921,7 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.f, win_size.y))
|
||||
{
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr) {
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id);
|
||||
wxQueueEvent(m_evt_handler, evt);
|
||||
}
|
||||
wxGetApp().printhost_job_queue().cancel(m_job_id - 1);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
@ -1023,7 +1014,7 @@ void NotificationManager::push_plater_warning_notification(const std::string& te
|
||||
auto notification = std::make_unique<NotificationManager::PlaterWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
push_notification_data(std::move(notification), 0);
|
||||
// dissaper if in preview
|
||||
set_in_preview(m_in_preview);
|
||||
apply_in_preview();
|
||||
}
|
||||
|
||||
void NotificationManager::close_plater_warning_notification(const std::string& text)
|
||||
@ -1129,28 +1120,21 @@ void NotificationManager::push_exporting_finished_notification(const std::string
|
||||
push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0);
|
||||
}
|
||||
|
||||
void NotificationManager::push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage)
|
||||
void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage)
|
||||
{
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text };
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, evt_handler, 0, id, filesize), 0);
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize), 0);
|
||||
}
|
||||
void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
|
||||
{
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
// bool found = false;
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
|
||||
dynamic_cast<PrintHostUploadNotification*>(notification.get())->set_percentage(percentage);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
// found = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (!found) {
|
||||
push_upload_job_notification(id, filename, host, percentage);
|
||||
}
|
||||
*/
|
||||
}
|
||||
void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host)
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ public:
|
||||
// Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button
|
||||
void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable);
|
||||
// notification with progress bar
|
||||
void push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
|
||||
void push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
|
||||
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
|
||||
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
@ -159,8 +159,10 @@ public:
|
||||
void render_notifications(GLCanvas3D& canvas, float overlay_width);
|
||||
// finds and closes all notifications of given type
|
||||
void close_notification_of_type(const NotificationType type);
|
||||
// Which view is active? Plater or G-code preview? Hide warnings in G-code preview.
|
||||
// Hides warnings in G-code preview. Should be called from plater only when 3d view/ preview is changed
|
||||
void set_in_preview(bool preview);
|
||||
// Calls set_in_preview to apply appearing or disappearing of some notificatons;
|
||||
void apply_in_preview() { set_in_preview(m_in_preview); }
|
||||
// Move to left to avoid colision with variable layer height gizmo.
|
||||
void set_move_from_overlay(bool move) { m_move_from_overlay = move; }
|
||||
// perform update_state on each notification and ask for more frames if needed, return true for render needed
|
||||
@ -319,7 +321,7 @@ private:
|
||||
void set_large(bool l);
|
||||
bool get_large() { return m_is_large; }
|
||||
void set_print_info(const std::string &info);
|
||||
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width)
|
||||
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override
|
||||
{
|
||||
// This notification is always hidden if !large (means side bar is collapsed)
|
||||
if (!get_large() && !is_finished())
|
||||
@ -348,9 +350,9 @@ private:
|
||||
{
|
||||
public:
|
||||
PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
|
||||
virtual void close() { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
|
||||
void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
|
||||
void show() { m_state = EState::Unknown; }
|
||||
virtual void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
|
||||
void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
|
||||
void show() { m_state = EState::Unknown; }
|
||||
};
|
||||
|
||||
|
||||
@ -365,10 +367,10 @@ private:
|
||||
virtual void count_spaces() override;
|
||||
virtual void render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
virtual void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
const float win_pos_x, const float win_pos_y) ;
|
||||
virtual void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y)
|
||||
@ -400,16 +402,16 @@ private:
|
||||
m_has_cancel_button = true;
|
||||
}
|
||||
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; }
|
||||
virtual void set_percentage(float percent);
|
||||
virtual void set_percentage(float percent) override;
|
||||
void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
|
||||
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; }
|
||||
protected:
|
||||
virtual void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
virtual void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
// Identifies job in cancel callback
|
||||
int m_job_id;
|
||||
// Size of uploaded size to be displayed in MB
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "slic3r/Utils/Platform.hpp"
|
||||
|
||||
#include "libslic3r/Platform.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
@ -324,7 +325,7 @@ void OpenGLManager::detect_multisample(int* attribList)
|
||||
enable_multisample &&
|
||||
// Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled,
|
||||
// at least on some platforms.
|
||||
(platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) &&
|
||||
platform_flavor() != PlatformFlavor::LinuxOnChromium &&
|
||||
wxGLCanvas::IsDisplaySupported(attribList)
|
||||
? EMultisampleState::Enabled : EMultisampleState::Disabled;
|
||||
// Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows
|
||||
|
@ -76,7 +76,6 @@
|
||||
#include "../Utils/FixModelByWin10.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
#include "../Utils/PresetUpdater.hpp"
|
||||
#include "../Utils/Platform.hpp"
|
||||
#include "../Utils/Process.hpp"
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "InstanceCheck.hpp"
|
||||
@ -89,7 +88,9 @@
|
||||
|
||||
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
|
||||
#include "WipeTowerDialog.hpp"
|
||||
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
|
||||
using boost::optional;
|
||||
namespace fs = boost::filesystem;
|
||||
@ -3660,7 +3661,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
show_action_buttons(false);
|
||||
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path,
|
||||
// Don't offer the "Eject" button on ChromeOS, the Linux side has no control over it.
|
||||
platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium);
|
||||
platform_flavor() != PlatformFlavor::LinuxOnChromium);
|
||||
wxGetApp().removable_drive_manager()->set_exporting_finished(true);
|
||||
}else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error)
|
||||
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false);
|
||||
@ -4788,7 +4789,8 @@ void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
|
||||
void Plater::reset() { p->reset(); }
|
||||
void Plater::reset_with_confirm()
|
||||
{
|
||||
if (wxMessageDialog(static_cast<wxWindow*>(this), _L("All objects will be removed, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Delete all"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
|
||||
if (p->model.objects.empty() ||
|
||||
wxMessageDialog(static_cast<wxWindow*>(this), _L("All objects will be removed, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Delete all"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,6 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
const ConfigOptionFloatOrPercent *first_layer_extrusion_width_ptr = (first_layer && first_layer_extrusion_width.value > 0) ?
|
||||
&first_layer_extrusion_width : nullptr;
|
||||
const float lh = float(first_layer ? first_layer_height : layer_height);
|
||||
const float bfr = bridging ? bridge_flow_ratio : 0.f;
|
||||
double max_flow = 0.;
|
||||
std::string max_flow_extrusion_type;
|
||||
auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) {
|
||||
|
@ -224,8 +224,8 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
std::vector<int> size;
|
||||
SetSize(load_user_data(UDT_SIZE, size) ? wxSize(size[0] * em, size[1] * em) : wxSize(HEIGHT * em, WIDTH * em));
|
||||
|
||||
Bind(wxEVT_SIZE, [this, em](wxSizeEvent& evt) {
|
||||
OnSize(evt);
|
||||
Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
|
||||
OnSize(evt);
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
});
|
||||
|
||||
@ -233,7 +233,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
if (load_user_data(UDT_POSITION, pos))
|
||||
SetPosition(wxPoint(pos[0], pos[1]));
|
||||
|
||||
Bind(wxEVT_MOVE, [this, em](wxMoveEvent& evt) {
|
||||
Bind(wxEVT_MOVE, [this](wxMoveEvent& evt) {
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
});
|
||||
|
||||
@ -245,7 +245,6 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
|
||||
const JobState state = get_state(selected);
|
||||
if (state < ST_ERROR) {
|
||||
// TODO: cancel
|
||||
GUI::wxGetApp().printhost_job_queue().cancel(selected);
|
||||
}
|
||||
});
|
||||
@ -282,7 +281,7 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||
// Both strings are UTF-8 encoded.
|
||||
upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string());
|
||||
|
||||
//wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 0, job.upload_data.upload_path.string(), job.printhost->get_host());
|
||||
wxGetApp().notification_manager()->push_upload_job_notification(job_list->GetItemCount(), (float)size_i / 1024 / 1024, job.upload_data.upload_path.string(), job.printhost->get_host());
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
@ -354,7 +353,7 @@ void PrintHostQueueDialog::on_progress(Event &evt)
|
||||
wxVariant nm, hst;
|
||||
job_list->GetValue(nm, evt.job_id, COL_FILENAME);
|
||||
job_list->GetValue(hst, evt.job_id, COL_HOST);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), 100 / evt.progress);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), evt.progress / 100.f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,7 +408,6 @@ void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, st
|
||||
void PrintHostQueueDialog::save_user_data(int udt)
|
||||
{
|
||||
const auto em = GetTextExtent("m").x;
|
||||
BOOST_LOG_TRIVIAL(error) << "save" << this->GetSize().x / em << " " << this->GetSize().y / em << " " << this->GetPosition().x << " " << this->GetPosition().y;
|
||||
auto *app_config = wxGetApp().app_config;
|
||||
if (udt & UserDataType::UDT_SIZE) {
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "slic3r/Utils/Platform.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
#include <boost/nowide/convert.hpp>
|
||||
@ -186,8 +186,13 @@ namespace search_for_drives_internal
|
||||
{
|
||||
//confirms if the file is removable drive and adds it to vector
|
||||
|
||||
//if not same file system - could be removable drive
|
||||
if (! compare_filesystem_id(path, parent_path)) {
|
||||
if (
|
||||
#ifdef __linux__
|
||||
// Chromium mounts removable drives in a way that produces the same device ID.
|
||||
platform_flavor() == PlatformFlavor::LinuxOnChromium ||
|
||||
#endif
|
||||
// If not same file system - could be removable drive.
|
||||
! compare_filesystem_id(path, parent_path)) {
|
||||
//free space
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::space_info si = boost::filesystem::space(path, ec);
|
||||
@ -232,7 +237,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
|
||||
|
||||
#else
|
||||
|
||||
if (platform() == Platform::Linux && platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||
if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||
// ChromeOS specific: search /mnt/chromeos/removable/* folder
|
||||
search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives);
|
||||
} else {
|
||||
@ -452,7 +457,7 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
out.has_eject =
|
||||
// Cannot control eject on Chromium.
|
||||
(platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) &&
|
||||
platform_flavor() != PlatformFlavor::LinuxOnChromium &&
|
||||
this->find_last_save_path_drive_data() != m_current_drives.end();
|
||||
out.has_removable_drives = ! m_current_drives.empty();
|
||||
}
|
||||
|
@ -1443,7 +1443,7 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Fuzzy skin (experimental)"));
|
||||
Option option = optgroup->get_option("fuzzy_skin");
|
||||
option.opt.width = 30;
|
||||
// option.opt.width = 30;
|
||||
optgroup->append_single_option_line(option);
|
||||
optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_thickness"));
|
||||
optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_point_dist"));
|
||||
@ -3841,7 +3841,7 @@ bool Tab::validate_custom_gcodes()
|
||||
assert(opt_group->opt_map().size() == 1);
|
||||
std::string key = opt_group->opt_map().begin()->first;
|
||||
std::string value = boost::any_cast<std::string>(opt_group->get_value(key));
|
||||
std::string config_value = m_config->opt_string(key);
|
||||
std::string config_value = m_type == Preset::TYPE_FILAMENT ? m_config->opt_string(key, 0u) : m_config->opt_string(key);
|
||||
valid &= validate_custom_gcode(opt_group->title, value);
|
||||
Field* field = opt_group->get_field(key);
|
||||
TextCtrl* text_ctrl = dynamic_cast<TextCtrl*>(field);
|
||||
|
Loading…
Reference in New Issue
Block a user