Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_window

This commit is contained in:
enricoturri1966 2021-03-22 08:39:44 +01:00
commit b6470c3390
32 changed files with 1612 additions and 994 deletions

View File

@ -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.

View File

@ -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

View File

@ -141,6 +141,8 @@ add_library(libslic3r STATIC
PerimeterGenerator.hpp
PlaceholderParser.cpp
PlaceholderParser.hpp
Platform.cpp
Platform.hpp
Point.cpp
Point.hpp
Polygon.cpp

View File

@ -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(

View File

@ -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;

View File

@ -74,6 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
const PrintRegionConfig &region_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);

View File

@ -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);
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View 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

View File

@ -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;

View File

@ -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;

View File

@ -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 &params)
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) {

View File

@ -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;
}

View File

@ -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));

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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();
}

View File

@ -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);