mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 08:41:11 -06:00
Merge remote-tracking branch 'origin/master' into ys_unsaved_changes
This commit is contained in:
commit
4d053cc4ee
148 changed files with 13789 additions and 3328 deletions
|
@ -99,6 +99,8 @@ add_library(libslic3r STATIC
|
|||
GCode/ToolOrdering.hpp
|
||||
GCode/WipeTower.cpp
|
||||
GCode/WipeTower.hpp
|
||||
GCode/GCodeProcessor.cpp
|
||||
GCode/GCodeProcessor.hpp
|
||||
GCode.cpp
|
||||
GCode.hpp
|
||||
GCodeReader.cpp
|
||||
|
@ -161,6 +163,8 @@ add_library(libslic3r STATIC
|
|||
PrintConfig.hpp
|
||||
PrintObject.cpp
|
||||
PrintRegion.cpp
|
||||
PNGRead.hpp
|
||||
PNGRead.cpp
|
||||
Semver.cpp
|
||||
ShortestPath.cpp
|
||||
ShortestPath.hpp
|
||||
|
@ -308,6 +312,8 @@ target_link_libraries(libslic3r
|
|||
TBB::tbb
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
PNG::PNG
|
||||
ZLIB::ZLIB
|
||||
)
|
||||
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
|
|
|
@ -8,7 +8,16 @@
|
|||
#include "SVG.hpp"
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
// Profiling support using the Shiny intrusive profiler
|
||||
//#define CLIPPER_UTILS_PROFILE
|
||||
#if defined(SLIC3R_PROFILE) && defined(CLIPPER_UTILS_PROFILE)
|
||||
#include <Shiny/Shiny.h>
|
||||
#define CLIPPERUTILS_PROFILE_FUNC() PROFILE_FUNC()
|
||||
#define CLIPPERUTILS_PROFILE_BLOCK(name) PROFILE_BLOCK(name)
|
||||
#else
|
||||
#define CLIPPERUTILS_PROFILE_FUNC()
|
||||
#define CLIPPERUTILS_PROFILE_BLOCK(name)
|
||||
#endif
|
||||
|
||||
#define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f)
|
||||
|
||||
|
@ -50,7 +59,7 @@ err:
|
|||
|
||||
void scaleClipperPolygon(ClipperLib::Path &polygon)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
|
||||
pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
pit->Y <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
|
@ -59,7 +68,7 @@ void scaleClipperPolygon(ClipperLib::Path &polygon)
|
|||
|
||||
void scaleClipperPolygons(ClipperLib::Paths &polygons)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
|
||||
pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
|
@ -69,7 +78,7 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons)
|
|||
|
||||
void unscaleClipperPolygon(ClipperLib::Path &polygon)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
|
||||
pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
|
@ -80,7 +89,7 @@ void unscaleClipperPolygon(ClipperLib::Path &polygon)
|
|||
|
||||
void unscaleClipperPolygons(ClipperLib::Paths &polygons)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
|
||||
pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
|
@ -790,7 +799,7 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
|
|||
|
||||
void safety_offset(ClipperLib::Paths* paths)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
|
||||
// scale input
|
||||
scaleClipperPolygons(*paths);
|
||||
|
@ -812,11 +821,11 @@ void safety_offset(ClipperLib::Paths* paths)
|
|||
if (! ccw)
|
||||
std::reverse(path.begin(), path.end());
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_AddPaths);
|
||||
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_AddPaths);
|
||||
co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
}
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_Execute);
|
||||
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute);
|
||||
// offset outside by 10um
|
||||
ClipperLib::Paths out_this;
|
||||
co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE));
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#include "CustomGCode.hpp"
|
||||
#include "Config.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCode.hpp"
|
||||
#else
|
||||
#include "GCode/PreviewData.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "GCodeWriter.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -17,8 +21,12 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint
|
|||
return;
|
||||
if (info.gcodes.empty() && ! colorprint_heights->values.empty()) {
|
||||
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
|
||||
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
|
||||
const auto& colorprint_values = colorprint_heights->values;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
const std::vector<std::string>& colors = ColorPrintColors::get();
|
||||
#else
|
||||
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
const auto& colorprint_values = colorprint_heights->values;
|
||||
info.gcodes.clear();
|
||||
info.gcodes.reserve(colorprint_values.size());
|
||||
int i = 0;
|
||||
|
|
|
@ -306,7 +306,11 @@ double ExtrusionLoop::min_mm3_per_mm() const
|
|||
std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
|
||||
{
|
||||
switch (role) {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
case erNone : return L("Unknown");
|
||||
#else
|
||||
case erNone : return L("None");
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
case erPerimeter : return L("Perimeter");
|
||||
case erExternalPerimeter : return L("External perimeter");
|
||||
case erOverhangPerimeter : return L("Overhang perimeter");
|
||||
|
@ -327,4 +331,40 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
|
|||
return "";
|
||||
}
|
||||
|
||||
ExtrusionRole ExtrusionEntity::string_to_role(const std::string& role)
|
||||
{
|
||||
if (role == L("Perimeter"))
|
||||
return erPerimeter;
|
||||
else if (role == L("External perimeter"))
|
||||
return erExternalPerimeter;
|
||||
else if (role == L("Overhang perimeter"))
|
||||
return erOverhangPerimeter;
|
||||
else if (role == L("Internal infill"))
|
||||
return erInternalInfill;
|
||||
else if (role == L("Solid infill"))
|
||||
return erSolidInfill;
|
||||
else if (role == L("Top solid infill"))
|
||||
return erTopSolidInfill;
|
||||
else if (role == L("Ironing"))
|
||||
return erIroning;
|
||||
else if (role == L("Bridge infill"))
|
||||
return erBridgeInfill;
|
||||
else if (role == L("Gap fill"))
|
||||
return erGapFill;
|
||||
else if (role == L("Skirt"))
|
||||
return erSkirt;
|
||||
else if (role == L("Support material"))
|
||||
return erSupportMaterial;
|
||||
else if (role == L("Support material interface"))
|
||||
return erSupportMaterialInterface;
|
||||
else if (role == L("Wipe tower"))
|
||||
return erWipeTower;
|
||||
else if (role == L("Custom"))
|
||||
return erCustom;
|
||||
else if (role == L("Mixed"))
|
||||
return erMixed;
|
||||
else
|
||||
return erNone;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ public:
|
|||
virtual double total_volume() const = 0;
|
||||
|
||||
static std::string role_to_string(ExtrusionRole role);
|
||||
static ExtrusionRole string_to_role(const std::string& role);
|
||||
};
|
||||
|
||||
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||
|
@ -121,8 +122,8 @@ public:
|
|||
// Height of the extrusion, used for visualization purposes.
|
||||
float height;
|
||||
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {};
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {};
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
|
||||
ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
|
|
|
@ -87,6 +87,7 @@ const char* TRANSFORM_ATTR = "transform";
|
|||
const char* PRINTABLE_ATTR = "printable";
|
||||
const char* INSTANCESCOUNT_ATTR = "instances_count";
|
||||
const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
|
||||
const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
|
||||
|
||||
const char* KEY_ATTR = "key";
|
||||
const char* VALUE_ATTR = "value";
|
||||
|
@ -285,6 +286,7 @@ namespace Slic3r {
|
|||
std::vector<float> vertices;
|
||||
std::vector<unsigned int> triangles;
|
||||
std::vector<std::string> custom_supports;
|
||||
std::vector<std::string> custom_seam;
|
||||
|
||||
bool empty()
|
||||
{
|
||||
|
@ -296,6 +298,7 @@ namespace Slic3r {
|
|||
vertices.clear();
|
||||
triangles.clear();
|
||||
custom_supports.clear();
|
||||
custom_seam.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1114,6 +1117,15 @@ namespace Slic3r {
|
|||
float(std::atof(object_data_points[i+6].c_str())),
|
||||
float(std::atof(object_data_points[i+7].c_str())));
|
||||
}
|
||||
|
||||
// The holes are saved elevated above the mesh and deeper (bad idea indeed).
|
||||
// This is retained for compatibility.
|
||||
// Place the hole to the mesh and make it shallower to compensate.
|
||||
// The offset is 1 mm above the mesh.
|
||||
for (sla::DrainHole& hole : sla_drain_holes) {
|
||||
hole.pos += hole.normal.normalized();
|
||||
hole.height -= 1.f;
|
||||
}
|
||||
|
||||
if (!sla_drain_holes.empty())
|
||||
m_sla_drain_holes.insert(IdToSlaDrainHolesMap::value_type(object_id, sla_drain_holes));
|
||||
|
@ -1544,6 +1556,7 @@ namespace Slic3r {
|
|||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
|
||||
|
||||
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
|
||||
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1877,14 +1890,18 @@ namespace Slic3r {
|
|||
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
|
||||
volume->calculate_convex_hull();
|
||||
|
||||
// recreate custom supports from previously loaded attribute
|
||||
// recreate custom supports and seam from previously loaded attribute
|
||||
for (unsigned i=0; i<triangles_count; ++i) {
|
||||
size_t index = src_start_id/3 + i;
|
||||
assert(index < geometry.custom_supports.size());
|
||||
assert(index < geometry.custom_seam.size());
|
||||
if (! geometry.custom_supports[index].empty())
|
||||
volume->m_supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]);
|
||||
if (! geometry.custom_seam[index].empty())
|
||||
volume->m_seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]);
|
||||
}
|
||||
|
||||
|
||||
// apply the remaining volume's metadata
|
||||
for (const Metadata& metadata : volume_data.metadata)
|
||||
{
|
||||
|
@ -2401,6 +2418,10 @@ namespace Slic3r {
|
|||
if (! custom_supports_data_string.empty())
|
||||
stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" ";
|
||||
|
||||
std::string custom_seam_data_string = volume->m_seam_facets.get_triangle_as_string(i);
|
||||
if (! custom_seam_data_string.empty())
|
||||
stream << CUSTOM_SEAM_ATTR << "=\"" << custom_seam_data_string << "\" ";
|
||||
|
||||
stream << "/>\n";
|
||||
}
|
||||
}
|
||||
|
@ -2591,7 +2612,18 @@ namespace Slic3r {
|
|||
for (const ModelObject* object : model.objects)
|
||||
{
|
||||
++count;
|
||||
auto& drain_holes = object->sla_drain_holes;
|
||||
sla::DrainHoles drain_holes = object->sla_drain_holes;
|
||||
|
||||
// The holes were placed 1mm above the mesh in the first implementation.
|
||||
// This was a bad idea and the reference point was changed in 2.3 so
|
||||
// to be on the mesh exactly. The elevated position is still saved
|
||||
// in 3MFs for compatibility reasons.
|
||||
for (sla::DrainHole& hole : drain_holes) {
|
||||
hole.pos -= hole.normal.normalized();
|
||||
hole.height += 1.f;
|
||||
}
|
||||
|
||||
|
||||
if (!drain_holes.empty())
|
||||
{
|
||||
out += string_printf(fmt, count);
|
||||
|
|
|
@ -35,6 +35,6 @@ namespace Slic3r {
|
|||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
|
||||
|
||||
}; // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_3mf_hpp_ */
|
||||
|
|
|
@ -8,8 +8,316 @@
|
|||
#include "libslic3r/Zipper.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "libslic3r/SlicesToTriangleMesh.hpp"
|
||||
#include "libslic3r/MarchingSquares.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGRead.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace marchsq {
|
||||
|
||||
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
||||
using Rst = Slic3r::png::ImageGreyscale;
|
||||
|
||||
// The type of pixel cell in the raster
|
||||
using ValueType = uint8_t;
|
||||
|
||||
// Value at a given position
|
||||
static uint8_t get(const Rst &rst, size_t row, size_t col)
|
||||
{
|
||||
return rst.get(row, col);
|
||||
}
|
||||
|
||||
// Number of rows and cols of the raster
|
||||
static size_t rows(const Rst &rst) { return rst.rows; }
|
||||
static size_t cols(const Rst &rst) { return rst.cols; }
|
||||
};
|
||||
|
||||
} // namespace marchsq
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
|
||||
struct PNGBuffer { std::vector<uint8_t> buf; std::string fname; };
|
||||
struct ArchiveData {
|
||||
boost::property_tree::ptree profile, config;
|
||||
std::vector<PNGBuffer> images;
|
||||
};
|
||||
|
||||
static const constexpr char *CONFIG_FNAME = "config.ini";
|
||||
static const constexpr char *PROFILE_FNAME = "prusaslicer.ini";
|
||||
|
||||
boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip)
|
||||
{
|
||||
std::string buf(size_t(entry.m_uncomp_size), '\0');
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw std::runtime_error(zip.get_errorstr());
|
||||
|
||||
boost::property_tree::ptree tree;
|
||||
std::stringstream ss(buf);
|
||||
boost::property_tree::read_ini(ss, tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip,
|
||||
const std::string & name)
|
||||
{
|
||||
std::vector<uint8_t> buf(entry.m_uncomp_size);
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw std::runtime_error(zip.get_errorstr());
|
||||
|
||||
return {std::move(buf), (name.empty() ? entry.m_filename : name)};
|
||||
}
|
||||
|
||||
ArchiveData extract_sla_archive(const std::string &zipfname,
|
||||
const std::string &exclude)
|
||||
{
|
||||
ArchiveData arch;
|
||||
|
||||
// Little RAII
|
||||
struct Arch: public MZ_Archive {
|
||||
Arch(const std::string &fname) {
|
||||
if (!open_zip_reader(&arch, fname))
|
||||
throw std::runtime_error(get_errorstr());
|
||||
}
|
||||
|
||||
~Arch() { close_zip_reader(&arch); }
|
||||
} zip (zipfname);
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch);
|
||||
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
mz_zip_archive_file_stat entry;
|
||||
|
||||
if (mz_zip_reader_file_stat(&zip.arch, i, &entry))
|
||||
{
|
||||
std::string name = entry.m_filename;
|
||||
boost::algorithm::to_lower(name);
|
||||
|
||||
if (boost::algorithm::contains(name, exclude)) continue;
|
||||
|
||||
if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip);
|
||||
if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip);
|
||||
|
||||
if (boost::filesystem::path(name).extension().string() == ".png") {
|
||||
auto it = std::lower_bound(
|
||||
arch.images.begin(), arch.images.end(), PNGBuffer{{}, name},
|
||||
[](const PNGBuffer &r1, const PNGBuffer &r2) {
|
||||
return std::less<std::string>()(r1.fname, r2.fname);
|
||||
});
|
||||
|
||||
arch.images.insert(it, read_png(entry, zip, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||
double px_w, double px_h)
|
||||
{
|
||||
ExPolygons polys; polys.reserve(rings.size());
|
||||
|
||||
for (const marchsq::Ring &ring : rings) {
|
||||
Polygon poly; Points &pts = poly.points;
|
||||
pts.reserve(ring.size());
|
||||
|
||||
for (const marchsq::Coord &crd : ring)
|
||||
pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h));
|
||||
|
||||
polys.emplace_back(poly);
|
||||
}
|
||||
|
||||
// reverse the raster transformations
|
||||
return union_ex(polys);
|
||||
}
|
||||
|
||||
template<class Fn> void foreach_vertex(ExPolygon &poly, Fn &&fn)
|
||||
{
|
||||
for (auto &p : poly.contour.points) fn(p);
|
||||
for (auto &h : poly.holes)
|
||||
for (auto &p : h.points) fn(p);
|
||||
}
|
||||
|
||||
void invert_raster_trafo(ExPolygons & expolys,
|
||||
const sla::RasterBase::Trafo &trafo,
|
||||
coord_t width,
|
||||
coord_t height)
|
||||
{
|
||||
if (trafo.flipXY) std::swap(height, width);
|
||||
|
||||
for (auto &expoly : expolys) {
|
||||
if (trafo.mirror_y)
|
||||
foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); });
|
||||
|
||||
if (trafo.mirror_x)
|
||||
foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); });
|
||||
|
||||
expoly.translate(-trafo.center_x, -trafo.center_y);
|
||||
|
||||
if (trafo.flipXY)
|
||||
foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); });
|
||||
|
||||
if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) {
|
||||
expoly.contour.reverse();
|
||||
for (auto &h : expoly.holes) h.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RasterParams {
|
||||
sla::RasterBase::Trafo trafo; // Raster transformations
|
||||
coord_t width, height; // scaled raster dimensions (not resolution)
|
||||
double px_h, px_w; // pixel dimesions
|
||||
marchsq::Coord win; // marching squares window size
|
||||
};
|
||||
|
||||
RasterParams get_raster_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
|
||||
auto *opt_disp_rows = cfg.option<ConfigOptionInt>("display_pixels_y");
|
||||
auto *opt_disp_w = cfg.option<ConfigOptionFloat>("display_width");
|
||||
auto *opt_disp_h = cfg.option<ConfigOptionFloat>("display_height");
|
||||
auto *opt_mirror_x = cfg.option<ConfigOptionBool>("display_mirror_x");
|
||||
auto *opt_mirror_y = cfg.option<ConfigOptionBool>("display_mirror_y");
|
||||
auto *opt_orient = cfg.option<ConfigOptionEnum<SLADisplayOrientation>>("display_orientation");
|
||||
|
||||
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
||||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
||||
throw std::runtime_error("Invalid SL1 file");
|
||||
|
||||
RasterParams rstp;
|
||||
|
||||
rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1);
|
||||
rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1);
|
||||
|
||||
rstp.trafo = sla::RasterBase::Trafo{opt_orient->value == sladoLandscape ?
|
||||
sla::RasterBase::roLandscape :
|
||||
sla::RasterBase::roPortrait,
|
||||
{opt_mirror_x->value, opt_mirror_y->value}};
|
||||
|
||||
rstp.height = scaled(opt_disp_h->value);
|
||||
rstp.width = scaled(opt_disp_w->value);
|
||||
|
||||
return rstp;
|
||||
}
|
||||
|
||||
struct SliceParams { double layerh = 0., initial_layerh = 0.; };
|
||||
|
||||
SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_layerh = cfg.option<ConfigOptionFloat>("layer_height");
|
||||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||
|
||||
if (!opt_layerh || !opt_init_layerh)
|
||||
throw std::runtime_error("Invalid SL1 file");
|
||||
|
||||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> extract_slices_from_sla_archive(
|
||||
ArchiveData & arch,
|
||||
const RasterParams & rstp,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
auto jobdir = arch.config.get<std::string>("jobDir");
|
||||
for (auto &c : jobdir) c = std::tolower(c);
|
||||
|
||||
std::vector<ExPolygons> slices(arch.images.size());
|
||||
|
||||
struct Status
|
||||
{
|
||||
double incr, val, prev;
|
||||
bool stop = false;
|
||||
tbb::spin_mutex mutex;
|
||||
} st {100. / slices.size(), 0., 0.};
|
||||
|
||||
tbb::parallel_for(size_t(0), arch.images.size(),
|
||||
[&arch, &slices, &st, &rstp, progr](size_t i) {
|
||||
// Status indication guarded with the spinlock
|
||||
{
|
||||
std::lock_guard<tbb::spin_mutex> lck(st.mutex);
|
||||
if (st.stop) return;
|
||||
|
||||
st.val += st.incr;
|
||||
double curr = std::round(st.val);
|
||||
if (curr > st.prev) {
|
||||
st.prev = curr;
|
||||
st.stop = !progr(int(curr));
|
||||
}
|
||||
}
|
||||
|
||||
png::ImageGreyscale img;
|
||||
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
|
||||
if (!png::decode_png(rb, img)) return;
|
||||
|
||||
auto rings = marchsq::execute(img, 128, rstp.win);
|
||||
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
|
||||
|
||||
// Invert the raster transformations indicated in
|
||||
// the profile metadata
|
||||
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
||||
|
||||
slices[i] = std::move(expolys);
|
||||
});
|
||||
|
||||
if (st.stop) slices = {};
|
||||
|
||||
return slices;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
|
||||
{
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "png");
|
||||
out.load(arch.profile);
|
||||
}
|
||||
|
||||
void import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
TriangleMesh & out,
|
||||
DynamicPrintConfig & profile,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
// Ensure minimum window size for marching squares
|
||||
windowsize.x() = std::max(2, windowsize.x());
|
||||
windowsize.y() = std::max(2, windowsize.y());
|
||||
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
|
||||
profile.load(arch.profile);
|
||||
|
||||
RasterParams rstp = get_raster_params(profile);
|
||||
rstp.win = {windowsize.y(), windowsize.x()};
|
||||
|
||||
SliceParams slicp = get_slice_params(profile);
|
||||
|
||||
std::vector<ExPolygons> slices =
|
||||
extract_slices_from_sla_archive(arch, rstp, progr);
|
||||
|
||||
if (!slices.empty())
|
||||
out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
|
||||
}
|
||||
|
||||
using ConfMap = std::map<std::string, std::string>;
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -38,6 +38,24 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
||||
|
||||
void import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
TriangleMesh & out,
|
||||
DynamicPrintConfig & profile,
|
||||
std::function<bool(int)> progr = [](int) { return true; });
|
||||
|
||||
inline void import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
TriangleMesh & out,
|
||||
std::function<bool(int)> progr = [](int) { return true; })
|
||||
{
|
||||
DynamicPrintConfig profile;
|
||||
import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||
}
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,9 +13,13 @@
|
|||
#include "GCode/SpiralVase.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#else
|
||||
#include "GCode/Analyzer.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
@ -29,7 +33,9 @@ namespace Slic3r {
|
|||
|
||||
// Forward declarations.
|
||||
class GCode;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GCodePreviewData;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
namespace { struct Item; }
|
||||
struct PrintInstance;
|
||||
|
@ -138,6 +144,15 @@ private:
|
|||
double m_last_wipe_tower_print_z = 0.f;
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
class ColorPrintColors
|
||||
{
|
||||
static const std::vector<std::string> Colors;
|
||||
public:
|
||||
static const std::vector<std::string>& get() { return Colors; }
|
||||
};
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
class GCode {
|
||||
public:
|
||||
GCode() :
|
||||
|
@ -145,21 +160,33 @@ public:
|
|||
m_enable_loop_clipping(true),
|
||||
m_enable_cooling_markers(false),
|
||||
m_enable_extrusion_role_markers(false),
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_last_processor_extrusion_role(erNone),
|
||||
#else
|
||||
m_enable_analyzer(false),
|
||||
m_last_analyzer_extrusion_role(erNone),
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
m_layer_count(0),
|
||||
m_layer_index(-1),
|
||||
m_layer(nullptr),
|
||||
m_volumetric_speed(0),
|
||||
m_last_pos_defined(false),
|
||||
m_last_extrusion_role(erNone),
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_last_mm3_per_mm(0.0),
|
||||
m_last_width(0.0f),
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm),
|
||||
m_last_width(GCodeAnalyzer::Default_Width),
|
||||
m_last_height(GCodeAnalyzer::Default_Height),
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
m_brim_done(false),
|
||||
m_second_layer_things_done(false),
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
m_normal_time_estimator(GCodeTimeEstimator::Normal),
|
||||
m_silent_time_estimator(GCodeTimeEstimator::Silent),
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
m_silent_time_estimator_enabled(false),
|
||||
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
|
||||
{}
|
||||
|
@ -167,7 +194,11 @@ public:
|
|||
|
||||
// throws std::runtime_exception on error,
|
||||
// throws CanceledException through print->throw_if_canceled().
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#else
|
||||
void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||
const Vec2d& origin() const { return m_origin; }
|
||||
|
@ -327,11 +358,16 @@ private:
|
|||
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
||||
// The Pressure Equalizer removes the markers from the final G-code.
|
||||
bool m_enable_extrusion_role_markers;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Keeps track of the last extrusion role passed to the processor
|
||||
ExtrusionRole m_last_processor_extrusion_role;
|
||||
#else
|
||||
// Enableds the G-code Analyzer.
|
||||
// Extended markers will be added during G-code generation.
|
||||
// The G-code Analyzer will remove these comments from the final G-code.
|
||||
bool m_enable_analyzer;
|
||||
ExtrusionRole m_last_analyzer_extrusion_role;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// How many times will change_layer() be called?
|
||||
// change_layer() will update the progress bar.
|
||||
unsigned int m_layer_count;
|
||||
|
@ -344,10 +380,20 @@ private:
|
|||
double m_volumetric_speed;
|
||||
// Support for the extrusion role markers. Which marker is active?
|
||||
ExtrusionRole m_last_extrusion_role;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Support for G-Code Processor
|
||||
float m_last_height{ 0.0f };
|
||||
float m_last_layer_z{ 0.0f };
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
double m_last_mm3_per_mm;
|
||||
float m_last_width{ 0.0f };
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
#else
|
||||
// Support for G-Code Analyzer
|
||||
double m_last_mm3_per_mm;
|
||||
float m_last_width;
|
||||
float m_last_height;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
Point m_last_pos;
|
||||
bool m_last_pos_defined;
|
||||
|
@ -368,13 +414,20 @@ private:
|
|||
// Index of a last object copy extruded.
|
||||
std::pair<const PrintObject*, Point> m_last_obj_copy;
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
// Time estimators
|
||||
GCodeTimeEstimator m_normal_time_estimator;
|
||||
GCodeTimeEstimator m_silent_time_estimator;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
bool m_silent_time_estimator_enabled;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Processor
|
||||
GCodeProcessor m_processor;
|
||||
#else
|
||||
// Analyzer
|
||||
GCodeAnalyzer m_analyzer;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Write a string into a file.
|
||||
void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "Analyzer.hpp"
|
||||
#include "PreviewData.hpp"
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
static const std::string AXIS_STR = "XYZE";
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float INCHES_TO_MM = 25.4f;
|
||||
|
@ -350,7 +352,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
|
|||
if (delta_pos[E] < 0.0f)
|
||||
{
|
||||
if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f))
|
||||
type = GCodeMove::Move;
|
||||
type = GCodeMove::Move;
|
||||
else
|
||||
type = GCodeMove::Retract;
|
||||
}
|
||||
|
@ -651,7 +653,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
|
|||
return true;
|
||||
}
|
||||
|
||||
// color change tag
|
||||
// pause print tag
|
||||
pos = comment.find(Pause_Print_Tag);
|
||||
if (pos != comment.npos)
|
||||
{
|
||||
|
@ -659,7 +661,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
|
|||
return true;
|
||||
}
|
||||
|
||||
// color change tag
|
||||
// custom code tag
|
||||
pos = comment.find(Custom_Code_Tag);
|
||||
if (pos != comment.npos)
|
||||
{
|
||||
|
@ -667,7 +669,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
|
|||
return true;
|
||||
}
|
||||
|
||||
// color change tag
|
||||
// end pause print or custom code tag
|
||||
pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag);
|
||||
if (pos != comment.npos)
|
||||
{
|
||||
|
@ -1191,3 +1193,5 @@ size_t GCodeAnalyzer::memory_used() const
|
|||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef slic3r_GCode_Analyzer_hpp_
|
||||
#define slic3r_GCode_Analyzer_hpp_
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
|
@ -302,4 +304,6 @@ private:
|
|||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCode_Analyzer_hpp_ */
|
||||
|
|
2163
src/libslic3r/GCode/GCodeProcessor.cpp
Normal file
2163
src/libslic3r/GCode/GCodeProcessor.cpp
Normal file
File diff suppressed because it is too large
Load diff
559
src/libslic3r/GCode/GCodeProcessor.hpp
Normal file
559
src/libslic3r/GCode/GCodeProcessor.hpp
Normal file
|
@ -0,0 +1,559 @@
|
|||
#ifndef slic3r_GCodeProcessor_hpp_
|
||||
#define slic3r_GCodeProcessor_hpp_
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class EMoveType : unsigned char
|
||||
{
|
||||
Noop,
|
||||
Retract,
|
||||
Unretract,
|
||||
Tool_change,
|
||||
Color_change,
|
||||
Pause_Print,
|
||||
Custom_GCode,
|
||||
Travel,
|
||||
Extrude,
|
||||
Count
|
||||
};
|
||||
|
||||
struct PrintEstimatedTimeStatistics
|
||||
{
|
||||
enum class ETimeMode : unsigned char
|
||||
{
|
||||
Normal,
|
||||
Stealth,
|
||||
Count
|
||||
};
|
||||
|
||||
struct Mode
|
||||
{
|
||||
float time;
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
|
||||
std::vector<std::pair<EMoveType, float>> moves_times;
|
||||
std::vector<std::pair<ExtrusionRole, float>> roles_times;
|
||||
std::vector<float> layers_times;
|
||||
|
||||
void reset() {
|
||||
time = 0.0f;
|
||||
custom_gcode_times.clear();
|
||||
moves_times.clear();
|
||||
roles_times.clear();
|
||||
layers_times.clear();
|
||||
}
|
||||
};
|
||||
|
||||
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
|
||||
|
||||
PrintEstimatedTimeStatistics() { reset(); }
|
||||
|
||||
void reset() {
|
||||
for (auto m : modes) {
|
||||
m.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class GCodeProcessor
|
||||
{
|
||||
public:
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
static const std::string Height_Tag;
|
||||
static const std::string Layer_Change_Tag;
|
||||
static const std::string Color_Change_Tag;
|
||||
static const std::string Pause_Print_Tag;
|
||||
static const std::string Custom_Code_Tag;
|
||||
static const std::string First_Line_M73_Placeholder_Tag;
|
||||
static const std::string Last_Line_M73_Placeholder_Tag;
|
||||
static const std::string Estimated_Printing_Time_Placeholder_Tag;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
static const std::string Width_Tag;
|
||||
static const std::string Mm3_Per_Mm_Tag;
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
private:
|
||||
using AxisCoords = std::array<float, 4>;
|
||||
using ExtruderColors = std::vector<unsigned char>;
|
||||
|
||||
enum class EUnits : unsigned char
|
||||
{
|
||||
Millimeters,
|
||||
Inches
|
||||
};
|
||||
|
||||
enum class EPositioningType : unsigned char
|
||||
{
|
||||
Absolute,
|
||||
Relative
|
||||
};
|
||||
|
||||
struct CachedPosition
|
||||
{
|
||||
AxisCoords position; // mm
|
||||
float feedrate; // mm/s
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct CpColor
|
||||
{
|
||||
unsigned char counter;
|
||||
unsigned char current;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
public:
|
||||
struct FeedrateProfile
|
||||
{
|
||||
float entry{ 0.0f }; // mm/s
|
||||
float cruise{ 0.0f }; // mm/s
|
||||
float exit{ 0.0f }; // mm/s
|
||||
};
|
||||
|
||||
struct Trapezoid
|
||||
{
|
||||
float accelerate_until{ 0.0f }; // mm
|
||||
float decelerate_after{ 0.0f }; // mm
|
||||
float cruise_feedrate{ 0.0f }; // mm/sec
|
||||
|
||||
float acceleration_time(float entry_feedrate, float acceleration) const;
|
||||
float cruise_time() const;
|
||||
float deceleration_time(float distance, float acceleration) const;
|
||||
float cruise_distance() const;
|
||||
};
|
||||
|
||||
struct TimeBlock
|
||||
{
|
||||
struct Flags
|
||||
{
|
||||
bool recalculate{ false };
|
||||
bool nominal_length{ false };
|
||||
};
|
||||
|
||||
EMoveType move_type{ EMoveType::Noop };
|
||||
ExtrusionRole role{ erNone };
|
||||
unsigned int layer_id{ 0 };
|
||||
float distance{ 0.0f }; // mm
|
||||
float acceleration{ 0.0f }; // mm/s^2
|
||||
float max_entry_speed{ 0.0f }; // mm/s
|
||||
float safe_feedrate{ 0.0f }; // mm/s
|
||||
Flags flags;
|
||||
FeedrateProfile feedrate_profile;
|
||||
Trapezoid trapezoid;
|
||||
|
||||
// Calculates this block's trapezoid
|
||||
void calculate_trapezoid();
|
||||
|
||||
float time() const;
|
||||
};
|
||||
|
||||
private:
|
||||
struct TimeMachine
|
||||
{
|
||||
struct State
|
||||
{
|
||||
float feedrate; // mm/s
|
||||
float safe_feedrate; // mm/s
|
||||
AxisCoords axis_feedrate; // mm/s
|
||||
AxisCoords abs_axis_feedrate; // mm/s
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct CustomGCodeTime
|
||||
{
|
||||
bool needed;
|
||||
float cache;
|
||||
std::vector<std::pair<CustomGCode::Type, float>> times;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
bool enabled;
|
||||
float acceleration; // mm/s^2
|
||||
// hard limit for the acceleration, to which the firmware will clamp.
|
||||
float max_acceleration; // mm/s^2
|
||||
float extrude_factor_override_percentage;
|
||||
float time; // s
|
||||
std::string line_m73_mask;
|
||||
State curr;
|
||||
State prev;
|
||||
CustomGCodeTime gcode_time;
|
||||
std::vector<TimeBlock> blocks;
|
||||
std::vector<float> g1_times_cache;
|
||||
std::array<float, static_cast<size_t>(EMoveType::Count)> moves_time;
|
||||
std::array<float, static_cast<size_t>(ExtrusionRole::erCount)> roles_time;
|
||||
std::vector<float> layers_time;
|
||||
|
||||
void reset();
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
void calculate_time(size_t keep_last_n_blocks = 0);
|
||||
};
|
||||
|
||||
struct TimeProcessor
|
||||
{
|
||||
struct Planner
|
||||
{
|
||||
// Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks.
|
||||
// Let's be conservative and plan for newer boards with more memory.
|
||||
static constexpr size_t queue_size = 64;
|
||||
// The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added.
|
||||
// We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate.
|
||||
static constexpr size_t refresh_threshold = queue_size * 4;
|
||||
};
|
||||
|
||||
// extruder_id is currently used to correctly calculate filament load / unload times into the total print time.
|
||||
// This is currently only really used by the MK3 MMU2:
|
||||
// extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
|
||||
bool extruder_unloaded;
|
||||
// whether or not to export post-process the gcode to export lines M73 in it
|
||||
bool export_remaining_time_enabled;
|
||||
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope()
|
||||
bool machine_envelope_processing_enabled;
|
||||
MachineEnvelopeConfig machine_limits;
|
||||
// Additional load / unload times for a filament exchange sequence.
|
||||
std::vector<float> filament_load_times;
|
||||
std::vector<float> filament_unload_times;
|
||||
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines;
|
||||
|
||||
void reset();
|
||||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
void post_process(const std::string& filename);
|
||||
};
|
||||
|
||||
public:
|
||||
struct MoveVertex
|
||||
{
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole extrusion_role{ erNone };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() }; // mm
|
||||
float delta_extruder{ 0.0f }; // mm
|
||||
float feedrate{ 0.0f }; // mm/s
|
||||
float width{ 0.0f }; // mm
|
||||
float height{ 0.0f }; // mm
|
||||
float mm3_per_mm{ 0.0f };
|
||||
float fan_speed{ 0.0f }; // percentage
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
Pointfs bed_shape;
|
||||
std::string printer_settings_id;
|
||||
std::vector<std::string> extruder_colors;
|
||||
PrintEstimatedTimeStatistics time_statistics;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
long long time{ 0 };
|
||||
void reset()
|
||||
{
|
||||
time = 0;
|
||||
moves = std::vector<MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
extruder_colors = std::vector<std::string>();
|
||||
}
|
||||
#else
|
||||
void reset()
|
||||
{
|
||||
moves = std::vector<MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
extruder_colors = std::vector<std::string>();
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
struct DataChecker
|
||||
{
|
||||
struct Error
|
||||
{
|
||||
float value;
|
||||
float tag_value;
|
||||
ExtrusionRole role;
|
||||
};
|
||||
|
||||
std::string type;
|
||||
float threshold{ 0.01f };
|
||||
float last_tag_value{ 0.0f };
|
||||
unsigned int count{ 0 };
|
||||
std::vector<Error> errors;
|
||||
|
||||
DataChecker(const std::string& type, float threshold)
|
||||
: type(type), threshold(threshold)
|
||||
{}
|
||||
|
||||
void update(float value, ExtrusionRole role) {
|
||||
++count;
|
||||
if (last_tag_value != 0.0f) {
|
||||
if (std::abs(value - last_tag_value) / last_tag_value > threshold)
|
||||
errors.push_back({ value, last_tag_value, role });
|
||||
}
|
||||
}
|
||||
|
||||
void reset() { last_tag_value = 0.0f; errors.clear(); count = 0; }
|
||||
|
||||
std::pair<float, float> get_min() const {
|
||||
float delta_min = FLT_MAX;
|
||||
float perc_min = 0.0f;
|
||||
for (const Error& e : errors) {
|
||||
if (delta_min > e.value - e.tag_value) {
|
||||
delta_min = e.value - e.tag_value;
|
||||
perc_min = 100.0f * delta_min / e.tag_value;
|
||||
}
|
||||
}
|
||||
return { delta_min, perc_min };
|
||||
}
|
||||
|
||||
std::pair<float, float> get_max() const {
|
||||
float delta_max = -FLT_MAX;
|
||||
float perc_max = 0.0f;
|
||||
for (const Error& e : errors) {
|
||||
if (delta_max < e.value - e.tag_value) {
|
||||
delta_max = e.value - e.tag_value;
|
||||
perc_max = 100.0f * delta_max / e.tag_value;
|
||||
}
|
||||
}
|
||||
return { delta_max, perc_max };
|
||||
}
|
||||
|
||||
void output() const {
|
||||
if (!errors.empty()) {
|
||||
std::cout << type << ":\n";
|
||||
std::cout << "Errors: " << errors.size() << " (" << 100.0f * float(errors.size()) / float(count) << "%)\n";
|
||||
auto [min, perc_min] = get_min();
|
||||
auto [max, perc_max] = get_max();
|
||||
std::cout << "min: " << min << "(" << perc_min << "%) - max: " << max << "(" << perc_max << "%)\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
private:
|
||||
GCodeReader m_parser;
|
||||
|
||||
EUnits m_units;
|
||||
EPositioningType m_global_positioning_type;
|
||||
EPositioningType m_e_local_positioning_type;
|
||||
std::vector<Vec3f> m_extruder_offsets;
|
||||
GCodeFlavor m_flavor;
|
||||
|
||||
AxisCoords m_start_position; // mm
|
||||
AxisCoords m_end_position; // mm
|
||||
AxisCoords m_origin; // mm
|
||||
CachedPosition m_cached_position;
|
||||
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
float m_height; // mm
|
||||
float m_mm3_per_mm;
|
||||
float m_fan_speed; // percentage
|
||||
ExtrusionRole m_extrusion_role;
|
||||
unsigned char m_extruder_id;
|
||||
ExtruderColors m_extruder_colors;
|
||||
std::vector<float> m_filament_diameters;
|
||||
float m_extruded_last_z;
|
||||
unsigned int m_layer_id;
|
||||
CpColor m_cp_color;
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
Unknown,
|
||||
PrusaSlicer,
|
||||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
ideaMaker
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
EProducer m_producer;
|
||||
bool m_producers_enabled;
|
||||
|
||||
TimeProcessor m_time_processor;
|
||||
|
||||
Result m_result;
|
||||
static unsigned int s_result_id;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
DataChecker m_mm3_per_mm_compare{ "mm3_per_mm", 0.01f };
|
||||
DataChecker m_height_compare{ "height", 0.01f };
|
||||
DataChecker m_width_compare{ "width", 0.01f };
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
public:
|
||||
GCodeProcessor();
|
||||
|
||||
void apply_config(const PrintConfig& config);
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void enable_stealth_time_estimator(bool enabled);
|
||||
bool is_stealth_time_estimator_enabled() const {
|
||||
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled;
|
||||
}
|
||||
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
||||
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
|
||||
void reset();
|
||||
|
||||
const Result& get_result() const { return m_result; }
|
||||
Result&& extract_result() { return std::move(m_result); }
|
||||
|
||||
// Process the gcode contained in the file with the given filename
|
||||
void process_file(const std::string& filename);
|
||||
|
||||
float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const;
|
||||
|
||||
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
|
||||
private:
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Process tags embedded into comments
|
||||
void process_tags(const std::string& comment);
|
||||
bool process_producers_tags(const std::string& comment);
|
||||
bool process_prusaslicer_tags(const std::string& comment);
|
||||
bool process_cura_tags(const std::string& comment);
|
||||
bool process_simplify3d_tags(const std::string& comment);
|
||||
bool process_craftware_tags(const std::string& comment);
|
||||
bool process_ideamaker_tags(const std::string& comment);
|
||||
|
||||
bool detect_producer(const std::string& comment);
|
||||
|
||||
// Move
|
||||
void process_G0(const GCodeReader::GCodeLine& line);
|
||||
void process_G1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Retract
|
||||
void process_G10(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unretract
|
||||
void process_G11(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Inches
|
||||
void process_G20(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Millimeters
|
||||
void process_G21(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Firmware controlled Retract
|
||||
void process_G22(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Firmware controlled Unretract
|
||||
void process_G23(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Absolute Positioning
|
||||
void process_G90(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Relative Positioning
|
||||
void process_G91(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Position
|
||||
void process_G92(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Sleep or Conditional stop
|
||||
void process_M1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to absolute mode
|
||||
void process_M82(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to relative mode
|
||||
void process_M83(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set fan speed
|
||||
void process_M106(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Disable fan
|
||||
void process_M107(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set tool (Sailfish)
|
||||
void process_M108(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Recall stored home offsets
|
||||
void process_M132(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set tool (MakerWare)
|
||||
void process_M135(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set max printing acceleration
|
||||
void process_M201(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set maximum feedrate
|
||||
void process_M203(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set default acceleration
|
||||
void process_M204(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Advanced settings
|
||||
void process_M205(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extrude factor override percentage
|
||||
void process_M221(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Repetier: Store x, y and z position
|
||||
void process_M401(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Repetier: Go to stored position
|
||||
void process_M402(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set allowable instantaneous speed change
|
||||
void process_M566(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
void process_M702(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes T line (Select Tool)
|
||||
void process_T(const GCodeReader::GCodeLine& line);
|
||||
void process_T(const std::string& command);
|
||||
|
||||
void store_move_vertex(EMoveType type);
|
||||
|
||||
float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
|
||||
float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
|
||||
float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
||||
float get_filament_load_time(size_t extruder_id);
|
||||
float get_filament_unload_time(size_t extruder_id);
|
||||
|
||||
void process_custom_gcode_time(CustomGCode::Type code);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
|
||||
void update_estimated_times_stats();
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCodeProcessor_hpp_ */
|
||||
|
||||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
#define L(s) (s)
|
||||
|
||||
|
@ -516,3 +518,5 @@ Color operator * (float f, const Color& color)
|
|||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef slic3r_GCode_PreviewData_hpp_
|
||||
#define slic3r_GCode_PreviewData_hpp_
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
#include "../Point.hpp"
|
||||
|
@ -56,8 +58,7 @@ public:
|
|||
// Color mapping to convert a float into a smooth rainbow of 10 colors.
|
||||
class RangeBase
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
virtual void reset() = 0;
|
||||
virtual bool empty() const = 0;
|
||||
virtual float min() const = 0;
|
||||
|
@ -73,7 +74,7 @@ public:
|
|||
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
|
||||
class Range : public RangeBase
|
||||
{
|
||||
public:
|
||||
public:
|
||||
Range();
|
||||
|
||||
// RangeBase Overrides
|
||||
|
@ -97,8 +98,7 @@ public:
|
|||
template <typename EnumRangeType>
|
||||
class MultiRange : public RangeBase
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
void reset() override
|
||||
{
|
||||
bounds = decltype(bounds){};
|
||||
|
@ -160,8 +160,7 @@ public:
|
|||
mode.set(static_cast<std::size_t>(range_type_value), enable);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
// Interval bounds
|
||||
struct Bounds
|
||||
{
|
||||
|
@ -394,4 +393,6 @@ public:
|
|||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCode_PreviewData_hpp_ */
|
||||
|
|
|
@ -21,7 +21,11 @@ TODO LIST
|
|||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCodeProcessor.hpp"
|
||||
#else
|
||||
#include "Analyzer.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
#if defined(__linux) || defined(__GNUC__ )
|
||||
|
@ -47,36 +51,69 @@ public:
|
|||
m_extrusion_flow(0.f),
|
||||
m_preview_suppressed(false),
|
||||
m_elapsed_time(0.f),
|
||||
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_default_analyzer_line_width(line_width),
|
||||
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_gcode_flavor(flavor),
|
||||
m_filpar(filament_parameters)
|
||||
{
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
m_gcode += buf;
|
||||
sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str());
|
||||
m_gcode += buf;
|
||||
#else
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
m_gcode += buf;
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
|
||||
m_gcode += buf;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
change_analyzer_line_width(line_width);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
|
||||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
|
||||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for processor:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
|
||||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
|
||||
m_wipe_tower_width = width;
|
||||
|
@ -111,8 +148,13 @@ public:
|
|||
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
|
||||
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
|
||||
// is asked to suppres output of some lines, which look like extrusions.
|
||||
WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
|
||||
WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
|
||||
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
|
||||
WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
|
||||
#else
|
||||
WipeTowerWriter& suppress_preview() { m_preview_suppressed = true; return *this; }
|
||||
WipeTowerWriter& resume_preview() { m_preview_suppressed = false; return *this; }
|
||||
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
WipeTowerWriter& feedrate(float f)
|
||||
{
|
||||
|
@ -149,8 +191,14 @@ public:
|
|||
Vec2f rot(this->rotate(Vec2f(x,y))); // this is where we want to go
|
||||
|
||||
if (! m_preview_suppressed && e > 0.f && len > 0.f) {
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
change_analyzer_mm3_per_mm(len, e);
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
#else
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
change_analyzer_mm3_per_mm(len, e);
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
// This is left zero if it is a travel move.
|
||||
float width = e * m_filpar[0].filament_area / (len * m_layer_height);
|
||||
// Correct for the roundings of a squished extrusion.
|
||||
|
@ -411,7 +459,9 @@ private:
|
|||
float m_wipe_tower_depth = 0.f;
|
||||
unsigned m_last_fan_speed = 0;
|
||||
int current_temp = -1;
|
||||
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
const float m_default_analyzer_line_width;
|
||||
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
float m_used_filament_length = 0.f;
|
||||
GCodeFlavor m_gcode_flavor;
|
||||
const std::vector<WipeTower::FilamentParameters>& m_filpar;
|
||||
|
@ -852,8 +902,12 @@ void WipeTower::toolchange_Unload(
|
|||
const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
|
||||
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
writer.append("; CP TOOLCHANGE UNLOAD\n")
|
||||
.change_analyzer_line_width(line_width);
|
||||
.change_analyzer_line_width(line_width);
|
||||
#else
|
||||
writer.append("; CP TOOLCHANGE UNLOAD\n");
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
unsigned i = 0; // iterates through ramming_speed
|
||||
m_left_to_right = true; // current direction of ramming
|
||||
|
@ -930,7 +984,9 @@ void WipeTower::toolchange_Unload(
|
|||
}
|
||||
}
|
||||
Vec2f end_of_ramming(writer.x(),writer.y());
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// Retraction:
|
||||
float old_x = writer.x();
|
||||
|
|
|
@ -115,7 +115,12 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
|||
{
|
||||
std::ifstream f(file);
|
||||
std::string line;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_parsing_file = true;
|
||||
while (m_parsing_file && std::getline(f, line))
|
||||
#else
|
||||
while (std::getline(f, line))
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,9 @@ public:
|
|||
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
|
||||
|
||||
void parse_file(const std::string &file, callback_t callback);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void quit_parsing_file() { m_parsing_file = false; }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
float x() const { return m_position[X]; }
|
||||
|
@ -145,6 +148,9 @@ private:
|
|||
char m_extrusion_axis;
|
||||
float m_position[NUM_AXES];
|
||||
bool m_verbose;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
bool m_parsing_file{ false };
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float MILLISEC_TO_SEC = 0.001f;
|
||||
static const float INCHES_TO_MM = 25.4f;
|
||||
|
@ -1671,3 +1673,5 @@ namespace Slic3r {
|
|||
}
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "GCodeReader.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
#define ENABLE_MOVE_STATS 0
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -481,4 +483,6 @@ namespace Slic3r {
|
|||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCodeTimeEstimator_hpp_ */
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
#include "GCodeWriter.hpp"
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
#include "GCode/PreviewData.hpp"
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -1007,6 +1009,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
|
|||
for (ModelVolume* volume : volumes)
|
||||
{
|
||||
volume->m_supported_facets.clear();
|
||||
volume->m_seam_facets.clear();
|
||||
if (!volume->mesh().empty()) {
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.require_shared_vertices();
|
||||
|
@ -1112,6 +1115,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
const auto volume_matrix = volume->get_matrix();
|
||||
|
||||
volume->m_supported_facets.clear();
|
||||
volume->m_seam_facets.clear();
|
||||
|
||||
if (! volume->is_model_part()) {
|
||||
// Modifiers are not cut, but we still need to add the instance transformation
|
||||
|
@ -1831,7 +1835,7 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||
}
|
||||
|
||||
|
||||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, FacetSupportType type) const
|
||||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data);
|
||||
|
@ -1993,6 +1997,16 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject
|
|||
return false;
|
||||
}
|
||||
|
||||
bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new) {
|
||||
assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
|
||||
assert(mo.volumes.size() == mo_new.volumes.size());
|
||||
for (size_t i=0; i<mo.volumes.size(); ++i) {
|
||||
if (! mo_new.volumes[i]->m_seam_facets.is_same_as(mo.volumes[i]->m_seam_facets))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern bool model_has_multi_part_objects(const Model &model)
|
||||
{
|
||||
for (const ModelObject *model_object : model.objects)
|
||||
|
|
|
@ -394,7 +394,7 @@ enum class ModelVolumeType : int {
|
|||
SUPPORT_BLOCKER,
|
||||
};
|
||||
|
||||
enum class FacetSupportType : int8_t {
|
||||
enum class EnforcerBlockerType : int8_t {
|
||||
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits!
|
||||
NONE = 0,
|
||||
ENFORCER = 1,
|
||||
|
@ -407,7 +407,7 @@ public:
|
|||
|
||||
const std::map<int, std::vector<bool>>& get_data() const { return m_data; }
|
||||
bool set(const TriangleSelector& selector);
|
||||
indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const;
|
||||
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
void clear();
|
||||
std::string get_triangle_as_string(int i) const;
|
||||
void set_triangle_from_string(int triangle_id, const std::string& str);
|
||||
|
@ -464,6 +464,9 @@ public:
|
|||
// List of mesh facets to be supported/unsupported.
|
||||
FacetsAnnotation m_supported_facets;
|
||||
|
||||
// List of seam enforcers/blockers.
|
||||
FacetsAnnotation m_seam_facets;
|
||||
|
||||
// A parent object owning this modifier volume.
|
||||
ModelObject* get_object() const { return this->object; }
|
||||
ModelVolumeType type() const { return m_type; }
|
||||
|
@ -593,7 +596,7 @@ private:
|
|||
ObjectBase(other),
|
||||
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
|
||||
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
|
||||
m_supported_facets(other.m_supported_facets)
|
||||
m_supported_facets(other.m_supported_facets), m_seam_facets(other.m_seam_facets)
|
||||
{
|
||||
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
|
||||
assert(this->id() == other.id() && this->config.id() == other.config.id());
|
||||
|
@ -612,6 +615,7 @@ private:
|
|||
assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id());
|
||||
|
||||
m_supported_facets.clear();
|
||||
m_seam_facets.clear();
|
||||
}
|
||||
|
||||
ModelVolume& operator=(ModelVolume &rhs) = delete;
|
||||
|
@ -625,7 +629,7 @@ private:
|
|||
template<class Archive> void load(Archive &ar) {
|
||||
bool has_convex_hull;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
|
||||
m_is_splittable, has_convex_hull, m_supported_facets);
|
||||
m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets);
|
||||
cereal::load_by_value(ar, config);
|
||||
assert(m_mesh);
|
||||
if (has_convex_hull) {
|
||||
|
@ -639,7 +643,7 @@ private:
|
|||
template<class Archive> void save(Archive &ar) const {
|
||||
bool has_convex_hull = m_convex_hull.get() != nullptr;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
|
||||
m_is_splittable, has_convex_hull, m_supported_facets);
|
||||
m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets);
|
||||
cereal::save_by_value(ar, config);
|
||||
if (has_convex_hull)
|
||||
cereal::save_optional(ar, m_convex_hull);
|
||||
|
@ -904,6 +908,10 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const
|
|||
// The function assumes that volumes list is synchronized.
|
||||
extern bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new);
|
||||
|
||||
// Test whether the now ModelObject has newer custom seam data than the old one.
|
||||
// The function assumes that volumes list is synchronized.
|
||||
extern bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new);
|
||||
|
||||
// If the model has multi-part objects, then it is currently not supported by the SLA mode.
|
||||
// Either the model cannot be loaded, or a SLA printer has to be activated.
|
||||
extern bool model_has_multi_part_objects(const Model &model);
|
||||
|
|
100
src/libslic3r/PNGRead.cpp
Normal file
100
src/libslic3r/PNGRead.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "PNGRead.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdio>
|
||||
#include <png.h>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
struct PNGDescr {
|
||||
png_struct *png = nullptr; png_info *info = nullptr;
|
||||
|
||||
PNGDescr() = default;
|
||||
PNGDescr(const PNGDescr&) = delete;
|
||||
PNGDescr(PNGDescr&&) = delete;
|
||||
PNGDescr& operator=(const PNGDescr&) = delete;
|
||||
PNGDescr& operator=(PNGDescr&&) = delete;
|
||||
|
||||
~PNGDescr()
|
||||
{
|
||||
if (png && info) png_destroy_info_struct(png, &info);
|
||||
if (png) png_destroy_read_struct( &png, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_png(const ReadBuf &rb)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
#if PNG_LIBPNG_VER_MINOR <= 2
|
||||
// Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
|
||||
// a const pointer. It is not possible to cast away the const qualifier from
|
||||
// the input buffer so... yes... life is challenging...
|
||||
png_byte buf[PNG_SIG_BYTES];
|
||||
auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
|
||||
std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
|
||||
#else
|
||||
auto buf = static_cast<png_const_bytep>(rb.buf);
|
||||
#endif
|
||||
|
||||
return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
|
||||
}
|
||||
|
||||
// Buffer read callback for libpng. It provides an allocated output buffer and
|
||||
// the amount of data it desires to read from the input.
|
||||
void png_read_callback(png_struct *png_ptr,
|
||||
png_bytep outBytes,
|
||||
png_size_t byteCountToRead)
|
||||
{
|
||||
// Retrieve our input buffer through the png_ptr
|
||||
auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
|
||||
|
||||
if (!reader || !reader->is_ok()) return;
|
||||
|
||||
reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
|
||||
}
|
||||
|
||||
bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
|
||||
in_buf.read(sig.data(), PNG_SIG_BYTES);
|
||||
if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
|
||||
return false;
|
||||
|
||||
PNGDescr dsc;
|
||||
dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
if(!dsc.png) return false;
|
||||
|
||||
dsc.info = png_create_info_struct(dsc.png);
|
||||
if(!dsc.info) return false;
|
||||
|
||||
png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
|
||||
|
||||
// Tell that we have already read the first bytes to check the signature
|
||||
png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
|
||||
|
||||
png_read_info(dsc.png, dsc.info);
|
||||
|
||||
out_img.cols = png_get_image_width(dsc.png, dsc.info);
|
||||
out_img.rows = png_get_image_height(dsc.png, dsc.info);
|
||||
size_t color_type = png_get_color_type(dsc.png, dsc.info);
|
||||
size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
|
||||
return false;
|
||||
|
||||
out_img.buf.resize(out_img.rows * out_img.cols);
|
||||
|
||||
auto readbuf = static_cast<png_bytep>(out_img.buf.data());
|
||||
for (size_t r = 0; r < out_img.rows; ++r)
|
||||
png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::png
|
70
src/libslic3r/PNGRead.hpp
Normal file
70
src/libslic3r/PNGRead.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#ifndef PNGREAD_HPP
|
||||
#define PNGREAD_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
// Interface for an input stream of encoded png image data.
|
||||
struct IStream {
|
||||
virtual ~IStream() = default;
|
||||
virtual size_t read(std::uint8_t *outp, size_t amount) = 0;
|
||||
virtual bool is_ok() const = 0;
|
||||
};
|
||||
|
||||
// The output format of decode_png: a 2D pixel matrix stored continuously row
|
||||
// after row (row major layout).
|
||||
template<class PxT> struct Image {
|
||||
std::vector<PxT> buf;
|
||||
size_t rows, cols;
|
||||
PxT get(size_t row, size_t col) const { return buf[row * cols + col]; }
|
||||
};
|
||||
|
||||
using ImageGreyscale = Image<uint8_t>;
|
||||
|
||||
// Only decodes true 8 bit grayscale png images. Returns false for other formats
|
||||
// TODO (if needed): implement transformation of rgb images into grayscale...
|
||||
bool decode_png(IStream &stream, ImageGreyscale &out_img);
|
||||
|
||||
// TODO (if needed)
|
||||
// struct RGB { uint8_t r, g, b; };
|
||||
// using ImageRGB = Image<RGB>;
|
||||
// bool decode_png(IStream &stream, ImageRGB &img);
|
||||
|
||||
|
||||
// Encoded png data buffer: a simple read-only buffer and its size.
|
||||
struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; };
|
||||
|
||||
bool is_png(const ReadBuf &pngbuf);
|
||||
|
||||
template<class Img> bool decode_png(const ReadBuf &in_buf, Img &out_img)
|
||||
{
|
||||
struct ReadBufStream: public IStream {
|
||||
const ReadBuf &rbuf_ref; size_t pos = 0;
|
||||
|
||||
explicit ReadBufStream(const ReadBuf &buf): rbuf_ref{buf} {}
|
||||
|
||||
size_t read(std::uint8_t *outp, size_t amount) override
|
||||
{
|
||||
if (amount > rbuf_ref.sz - pos) return 0;
|
||||
|
||||
auto buf = static_cast<const std::uint8_t *>(rbuf_ref.buf);
|
||||
std::copy(buf + pos, buf + (pos + amount), outp);
|
||||
pos += amount;
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
bool is_ok() const override { return pos < rbuf_ref.sz; }
|
||||
} stream{in_buf};
|
||||
|
||||
return decode_png(stream, out_img);
|
||||
}
|
||||
|
||||
// TODO: std::istream of FILE* could be similarly adapted in case its needed...
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
|
||||
#endif // PNGREAD_HPP
|
|
@ -634,7 +634,9 @@ void PresetCollection::add_default_preset(const std::vector<std::string> &keys,
|
|||
// Throws an exception on error.
|
||||
void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
|
||||
{
|
||||
boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
||||
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
m_dir_path = dir.string();
|
||||
std::string errors_cummulative;
|
||||
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
|
||||
|
@ -1518,7 +1520,9 @@ PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector<std::str
|
|||
// Throws an exception on error.
|
||||
void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const std::string& subdir)
|
||||
{
|
||||
boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
||||
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
m_dir_path = dir.string();
|
||||
std::string errors_cummulative;
|
||||
// Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken.
|
||||
|
@ -1812,6 +1816,26 @@ namespace PresetUtils {
|
|||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string system_printer_bed_model(const Preset& preset)
|
||||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_model.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string system_printer_bed_texture(const Preset& preset)
|
||||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_texture.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
return out;
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
} // namespace PresetUtils
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -527,6 +527,10 @@ public:
|
|||
namespace PresetUtils {
|
||||
// PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile.
|
||||
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string system_printer_bed_model(const Preset& preset);
|
||||
std::string system_printer_bed_texture(const Preset& preset);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
} // namespace PresetUtils
|
||||
|
||||
|
||||
|
|
|
@ -404,6 +404,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
|
|||
mv_dst.name = mv_src.name;
|
||||
static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
|
||||
mv_dst.m_supported_facets = mv_src.m_supported_facets;
|
||||
mv_dst.m_seam_facets = mv_src.m_seam_facets;
|
||||
//FIXME what to do with the materials?
|
||||
// mv_dst.m_material_id = mv_src.m_material_id;
|
||||
++ i_src;
|
||||
|
@ -867,6 +868,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
model_volume_list_update_supports(model_object, model_object_new);
|
||||
}
|
||||
}
|
||||
if (model_custom_seam_data_changed(model_object, model_object_new)) {
|
||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||
}
|
||||
if (! model_parts_differ && ! modifiers_differ) {
|
||||
// Synchronize Object's config.
|
||||
bool object_config_changed = model_object.config != model_object_new.config;
|
||||
|
@ -1632,13 +1636,21 @@ void Print::process()
|
|||
// The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
// write error into the G-code, cannot execute post-processing scripts).
|
||||
// It is up to the caller to show an error message.
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
#else
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
{
|
||||
// output everything to a G-code file
|
||||
// The following call may die if the output_filename_format template substitution fails.
|
||||
std::string path = this->output_filepath(path_template);
|
||||
std::string message;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (!path.empty() && result == nullptr) {
|
||||
#else
|
||||
if (! path.empty() && preview_data == nullptr) {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// Only show the path if preview_data is not set -> running from command line.
|
||||
message = L("Exporting G-code");
|
||||
message += " to ";
|
||||
|
@ -1649,7 +1661,11 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa
|
|||
|
||||
// The following line may die for multiple reasons.
|
||||
GCode gcode;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
|
||||
#else
|
||||
gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
|
@ -2180,16 +2196,16 @@ DynamicConfig PrintStatistics::config() const
|
|||
DynamicConfig config;
|
||||
std::string normal_print_time = short_time(this->estimated_normal_print_time);
|
||||
std::string silent_print_time = short_time(this->estimated_silent_print_time);
|
||||
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
|
||||
config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament / 1000.));
|
||||
config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume));
|
||||
config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost));
|
||||
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
|
||||
config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.));
|
||||
config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume));
|
||||
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
|
||||
config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges));
|
||||
config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight));
|
||||
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost));
|
||||
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament));
|
||||
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
|
||||
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
|
||||
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "libslic3r.h"
|
||||
|
||||
|
@ -20,7 +23,9 @@ class Print;
|
|||
class PrintObject;
|
||||
class ModelObject;
|
||||
class GCode;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GCodePreviewData;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
enum class SlicingMode : uint32_t;
|
||||
class Layer;
|
||||
class SupportLayer;
|
||||
|
@ -186,10 +191,8 @@ public:
|
|||
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
|
||||
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||
|
||||
// Helpers to project custom supports on slices
|
||||
void project_and_append_custom_supports(FacetSupportType type, std::vector<ExPolygons>& expolys) const;
|
||||
void project_and_append_custom_enforcers(std::vector<ExPolygons>& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); }
|
||||
void project_and_append_custom_blockers(std::vector<ExPolygons>& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); }
|
||||
// Helpers to project custom facets on slices
|
||||
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const;
|
||||
|
||||
private:
|
||||
// to be called from Print only.
|
||||
|
@ -300,8 +303,10 @@ struct PrintStatistics
|
|||
PrintStatistics() { clear(); }
|
||||
std::string estimated_normal_print_time;
|
||||
std::string estimated_silent_print_time;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
std::vector<std::pair<CustomGCode::Type, std::string>> estimated_normal_custom_gcode_print_times;
|
||||
std::vector<std::pair<CustomGCode::Type, std::string>> estimated_silent_custom_gcode_print_times;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
double total_used_filament;
|
||||
double total_extruded_volume;
|
||||
double total_cost;
|
||||
|
@ -319,10 +324,12 @@ struct PrintStatistics
|
|||
std::string finalize_output_path(const std::string &path_in) const;
|
||||
|
||||
void clear() {
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
estimated_normal_print_time.clear();
|
||||
estimated_silent_print_time.clear();
|
||||
estimated_normal_custom_gcode_print_times.clear();
|
||||
estimated_silent_custom_gcode_print_times.clear();
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
total_used_filament = 0.;
|
||||
total_extruded_volume = 0.;
|
||||
total_cost = 0.;
|
||||
|
@ -362,7 +369,11 @@ public:
|
|||
void process() override;
|
||||
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
||||
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#else
|
||||
std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// methods for handling state
|
||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||
|
@ -417,6 +428,7 @@ public:
|
|||
const Polygon& first_layer_convex_hull() const { return m_first_layer_convex_hull; }
|
||||
|
||||
const PrintStatistics& print_statistics() const { return m_print_statistics; }
|
||||
PrintStatistics& print_statistics() { return m_print_statistics; }
|
||||
|
||||
// Wipe tower support.
|
||||
bool has_wipe_tower() const;
|
||||
|
|
|
@ -507,9 +507,9 @@ protected:
|
|||
bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||
PrintStateBase::TimeStamp set_done(PrintStepEnum step) {
|
||||
std::pair<PrintStateBase::TimeStamp, bool> status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); });
|
||||
if (status.second)
|
||||
this->status_update_warnings(this->id(), static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
if (status.second)
|
||||
this->status_update_warnings(this->id(), static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
}
|
||||
bool invalidate_step(PrintStepEnum step)
|
||||
{ return m_state.invalidate(step, this->cancel_callback()); }
|
||||
|
@ -556,9 +556,9 @@ protected:
|
|||
{ return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); }
|
||||
PrintStateBase::TimeStamp set_done(PrintObjectStepEnum step) {
|
||||
std::pair<PrintStateBase::TimeStamp, bool> status = m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); });
|
||||
if (status.second)
|
||||
this->status_update_warnings(m_print, static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
if (status.second)
|
||||
this->status_update_warnings(m_print, static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
}
|
||||
|
||||
bool invalidate_step(PrintObjectStepEnum step)
|
||||
|
|
|
@ -3530,6 +3530,12 @@ CLIActionsConfigDef::CLIActionsConfigDef()
|
|||
def->cli = "export-gcode|gcode|g";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("gcodeviewer", coBool);
|
||||
def->label = L("G-code viewer");
|
||||
def->tooltip = L("Visualize an already sliced and saved G-code");
|
||||
def->cli = "gcodeviewer";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("slice", coBool);
|
||||
def->label = L("Slice");
|
||||
def->tooltip = L("Slice the model as FFF or SLA based on the printer_technology configuration value.");
|
||||
|
|
|
@ -239,9 +239,13 @@ class DynamicPrintConfig : public DynamicConfig
|
|||
public:
|
||||
DynamicPrintConfig() {}
|
||||
DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {}
|
||||
DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {}
|
||||
explicit DynamicPrintConfig(const StaticPrintConfig &rhs);
|
||||
explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {}
|
||||
|
||||
DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; }
|
||||
DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; }
|
||||
|
||||
static DynamicPrintConfig full_print_config();
|
||||
static DynamicPrintConfig* new_from_defaults_keys(const std::vector<std::string> &keys);
|
||||
|
||||
|
|
|
@ -2669,12 +2669,14 @@ void PrintObject::_generate_support_material()
|
|||
}
|
||||
|
||||
|
||||
void PrintObject::project_and_append_custom_supports(
|
||||
FacetSupportType type, std::vector<ExPolygons>& expolys) const
|
||||
void PrintObject::project_and_append_custom_facets(
|
||||
bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const
|
||||
{
|
||||
for (const ModelVolume* mv : this->model_object()->volumes) {
|
||||
const indexed_triangle_set custom_facets = mv->m_supported_facets.get_facets(*mv, type);
|
||||
if (custom_facets.indices.empty())
|
||||
const indexed_triangle_set custom_facets = seam
|
||||
? mv->m_seam_facets.get_facets(*mv, type)
|
||||
: mv->m_supported_facets.get_facets(*mv, type);
|
||||
if (! mv->is_model_part() || custom_facets.indices.empty())
|
||||
continue;
|
||||
|
||||
const Transform3f& tr1 = mv->get_matrix().cast<float>();
|
||||
|
@ -2721,7 +2723,7 @@ void PrintObject::project_and_append_custom_supports(
|
|||
|
||||
// Ignore triangles with upward-pointing normal. Don't forget about mirroring.
|
||||
float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z();
|
||||
if (tr_det_sign * z_comp > 0.)
|
||||
if (! seam && tr_det_sign * z_comp > 0.)
|
||||
continue;
|
||||
|
||||
// Sort the three vertices according to z-coordinate.
|
||||
|
|
|
@ -128,12 +128,13 @@ protected:
|
|||
}
|
||||
|
||||
public:
|
||||
template<class GammaFn> AGGRaster(const Resolution &res,
|
||||
template<class GammaFn>
|
||||
AGGRaster(const Resolution &res,
|
||||
const PixelDim & pd,
|
||||
const Trafo & trafo,
|
||||
const TColor & foreground,
|
||||
const TColor & background,
|
||||
GammaFn && gammafn)
|
||||
const TColor & foreground,
|
||||
const TColor & background,
|
||||
GammaFn && gammafn)
|
||||
: m_resolution(res)
|
||||
, m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm)
|
||||
, m_buf(res.pixels())
|
||||
|
|
|
@ -58,6 +58,8 @@ struct DrainHole
|
|||
|
||||
using DrainHoles = std::vector<DrainHole>;
|
||||
|
||||
constexpr float HoleStickOutLength = 1.f;
|
||||
|
||||
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh &mesh,
|
||||
const HollowingConfig & = {},
|
||||
const JobController &ctl = {});
|
||||
|
|
|
@ -28,15 +28,9 @@ void reproject_support_points(const IndexedMesh &mesh, std::vector<PointType> &p
|
|||
inline void reproject_points_and_holes(ModelObject *object)
|
||||
{
|
||||
bool has_sppoints = !object->sla_support_points.empty();
|
||||
bool has_holes = !object->sla_drain_holes.empty();
|
||||
|
||||
// Disabling reprojection of holes as they have a significant offset away
|
||||
// from the model body which tolerates minor geometrical changes.
|
||||
//
|
||||
// TODO: uncomment and ensure the right offset of the hole points if
|
||||
// reprojection would still be necessary.
|
||||
// bool has_holes = !object->sla_drain_holes.empty();
|
||||
|
||||
if (!object || (/*!has_holes &&*/ !has_sppoints)) return;
|
||||
if (!object || (!has_holes && !has_sppoints)) return;
|
||||
|
||||
TriangleMesh rmsh = object->raw_mesh();
|
||||
rmsh.require_shared_vertices();
|
||||
|
@ -45,8 +39,8 @@ inline void reproject_points_and_holes(ModelObject *object)
|
|||
if (has_sppoints)
|
||||
reproject_support_points(emesh, object->sla_support_points);
|
||||
|
||||
// if (has_holes)
|
||||
// reproject_support_points(emesh, object->sla_drain_holes);
|
||||
if (has_holes)
|
||||
reproject_support_points(emesh, object->sla_drain_holes);
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -1181,6 +1181,12 @@ sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const
|
|||
hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)),
|
||||
hl.normal(1)/(sc(1)*sc(1)),
|
||||
hl.normal(2)/(sc(2)*sc(2)));
|
||||
|
||||
// Now shift the hole a bit above the object and make it deeper to
|
||||
// compensate for it. This is to avoid problems when the hole is placed
|
||||
// on (nearly) flat surface.
|
||||
hl.pos -= hl.normal.normalized() * sla::HoleStickOutLength;
|
||||
hl.height += sla::HoleStickOutLength;
|
||||
}
|
||||
|
||||
return pts;
|
||||
|
|
|
@ -972,8 +972,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
std::vector<ExPolygons> blockers = object.slice_support_blockers();
|
||||
|
||||
// Append custom supports.
|
||||
object.project_and_append_custom_enforcers(enforcers);
|
||||
object.project_and_append_custom_blockers(blockers);
|
||||
object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers);
|
||||
object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers);
|
||||
|
||||
// Output layers, sorted by top Z.
|
||||
MyLayersPtr contact_out;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#define ENABLE_RENDER_STATISTICS 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
|
||||
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
|
||||
#define ENABLE_RENDER_PICKING_PASS 0
|
||||
// Enable extracting thumbnails from selected gcode and save them as png files
|
||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
|
||||
|
@ -54,5 +54,10 @@
|
|||
// Enable built-in DPI changed event handler of wxWidgets 3.1.3
|
||||
#define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1)
|
||||
|
||||
// Enable G-Code viewer
|
||||
#define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1)
|
||||
#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER)
|
||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER)
|
||||
#define ENABLE_GCODE_VIEWER_TASKBAR_ICON (0 && ENABLE_GCODE_VIEWER)
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -35,7 +35,7 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si
|
|||
|
||||
void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
||||
const Vec3f& source, const Vec3f& dir,
|
||||
float radius, FacetSupportType new_state)
|
||||
float radius, EnforcerBlockerType new_state)
|
||||
{
|
||||
assert(facet_start < m_orig_size_indices);
|
||||
assert(is_approx(dir.norm(), 1.f));
|
||||
|
@ -77,7 +77,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|||
// the triangle recursively, selecting just subtriangles truly inside the circle.
|
||||
// This is done by an actual recursive call. Returns false if the triangle is
|
||||
// outside the cursor.
|
||||
bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call)
|
||||
bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call)
|
||||
{
|
||||
assert(facet_idx < int(m_triangles.size()));
|
||||
|
||||
|
@ -140,7 +140,7 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo
|
|||
|
||||
|
||||
|
||||
void TriangleSelector::set_facet(int facet_idx, FacetSupportType state)
|
||||
void TriangleSelector::set_facet(int facet_idx, EnforcerBlockerType state)
|
||||
{
|
||||
assert(facet_idx < m_orig_size_indices);
|
||||
undivide_triangle(facet_idx);
|
||||
|
@ -157,7 +157,7 @@ void TriangleSelector::split_triangle(int facet_idx)
|
|||
|
||||
Triangle* tr = &m_triangles[facet_idx];
|
||||
|
||||
FacetSupportType old_type = tr->get_state();
|
||||
EnforcerBlockerType old_type = tr->get_state();
|
||||
|
||||
if (tr->was_split_before() != 0) {
|
||||
// This triangle is not split at the moment, but was at one point
|
||||
|
@ -323,7 +323,7 @@ void TriangleSelector::remove_useless_children(int facet_idx)
|
|||
|
||||
|
||||
// Return if a child is not leaf or two children differ in type.
|
||||
FacetSupportType first_child_type = FacetSupportType::NONE;
|
||||
EnforcerBlockerType first_child_type = EnforcerBlockerType::NONE;
|
||||
for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) {
|
||||
if (m_triangles[tr.children[child_idx]].is_split())
|
||||
return;
|
||||
|
@ -456,7 +456,7 @@ void TriangleSelector::push_triangle(int a, int b, int c)
|
|||
}
|
||||
|
||||
|
||||
void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state)
|
||||
void TriangleSelector::perform_split(int facet_idx, EnforcerBlockerType old_state)
|
||||
{
|
||||
Triangle* tr = &m_triangles[facet_idx];
|
||||
|
||||
|
@ -520,7 +520,7 @@ void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state)
|
|||
|
||||
|
||||
|
||||
indexed_triangle_set TriangleSelector::get_facets(FacetSupportType state) const
|
||||
indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) const
|
||||
{
|
||||
indexed_triangle_set out;
|
||||
for (const Triangle& tr : m_triangles) {
|
||||
|
@ -542,7 +542,7 @@ std::map<int, std::vector<bool>> TriangleSelector::serialize() const
|
|||
{
|
||||
// Each original triangle of the mesh is assigned a number encoding its state
|
||||
// or how it is split. Each triangle is encoded by 4 bits (xxyy):
|
||||
// leaf triangle: xx = FacetSupportType, yy = 0
|
||||
// leaf triangle: xx = EnforcerBlockerType, yy = 0
|
||||
// non-leaf: xx = special side, yy = number of split sides
|
||||
// These are bitwise appended and formed into one 64-bit integer.
|
||||
|
||||
|
@ -553,7 +553,7 @@ std::map<int, std::vector<bool>> TriangleSelector::serialize() const
|
|||
for (int i=0; i<m_orig_size_indices; ++i) {
|
||||
const Triangle& tr = m_triangles[i];
|
||||
|
||||
if (! tr.is_split() && tr.get_state() == FacetSupportType::NONE)
|
||||
if (! tr.is_split() && tr.get_state() == EnforcerBlockerType::NONE)
|
||||
continue; // no need to save anything, unsplit and unselected is default
|
||||
|
||||
std::vector<bool> data; // complete encoding of this mesh triangle
|
||||
|
@ -627,7 +627,7 @@ void TriangleSelector::deserialize(const std::map<int, std::vector<bool>> data)
|
|||
int num_of_split_sides = (next_code & 0b11);
|
||||
int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0;
|
||||
bool is_split = num_of_children != 0;
|
||||
FacetSupportType state = FacetSupportType(next_code >> 2);
|
||||
EnforcerBlockerType state = EnforcerBlockerType(next_code >> 2);
|
||||
int special_side = (next_code >> 2);
|
||||
|
||||
// Take care of the first iteration separately, so handling of the others is simpler.
|
||||
|
@ -641,7 +641,7 @@ void TriangleSelector::deserialize(const std::map<int, std::vector<bool>> data)
|
|||
// then go to the next.
|
||||
parents.push_back({triangle_id, 0, num_of_children});
|
||||
m_triangles[triangle_id].set_division(num_of_children-1, special_side);
|
||||
perform_split(triangle_id, FacetSupportType::NONE);
|
||||
perform_split(triangle_id, EnforcerBlockerType::NONE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ void TriangleSelector::deserialize(const std::map<int, std::vector<bool>> data)
|
|||
const ProcessingInfo& last = parents.back();
|
||||
int this_idx = m_triangles[last.facet_id].children[last.processed_children];
|
||||
m_triangles[this_idx].set_division(num_of_children-1, special_side);
|
||||
perform_split(this_idx, FacetSupportType::NONE);
|
||||
perform_split(this_idx, EnforcerBlockerType::NONE);
|
||||
parents.push_back({this_idx, 0, num_of_children});
|
||||
} else {
|
||||
// this triangle belongs to last split one
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class FacetSupportType : int8_t;
|
||||
enum class EnforcerBlockerType : int8_t;
|
||||
|
||||
|
||||
|
||||
|
@ -29,13 +29,13 @@ public:
|
|||
const Vec3f& source, // camera position (mesh coords)
|
||||
const Vec3f& dir, // direction of the ray (mesh coords)
|
||||
float radius, // radius of the cursor
|
||||
FacetSupportType new_state); // enforcer or blocker?
|
||||
EnforcerBlockerType new_state); // enforcer or blocker?
|
||||
|
||||
// Get facets currently in the given state.
|
||||
indexed_triangle_set get_facets(FacetSupportType state) const;
|
||||
indexed_triangle_set get_facets(EnforcerBlockerType state) const;
|
||||
|
||||
// Set facet of the mesh to a given state. Only works for original triangles.
|
||||
void set_facet(int facet_idx, FacetSupportType state);
|
||||
void set_facet(int facet_idx, EnforcerBlockerType state);
|
||||
|
||||
// Clear everything and make the tree empty.
|
||||
void reset();
|
||||
|
@ -59,7 +59,7 @@ protected:
|
|||
// It increments/decrements reference counter on vertices.
|
||||
Triangle(int a, int b, int c)
|
||||
: verts_idxs{a, b, c},
|
||||
state{FacetSupportType(0)},
|
||||
state{EnforcerBlockerType(0)},
|
||||
number_of_splits{0},
|
||||
special_side_idx{0},
|
||||
old_number_of_splits{0}
|
||||
|
@ -77,8 +77,8 @@ protected:
|
|||
void set_division(int sides_to_split, int special_side_idx = -1);
|
||||
|
||||
// Get/set current state.
|
||||
void set_state(FacetSupportType type) { assert(! is_split()); state = type; }
|
||||
FacetSupportType get_state() const { assert(! is_split()); return state; }
|
||||
void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; }
|
||||
EnforcerBlockerType get_state() const { assert(! is_split()); return state; }
|
||||
|
||||
// Get info on how it's split.
|
||||
bool is_split() const { return number_of_split_sides() != 0; }
|
||||
|
@ -90,7 +90,7 @@ protected:
|
|||
private:
|
||||
int number_of_splits;
|
||||
int special_side_idx;
|
||||
FacetSupportType state;
|
||||
EnforcerBlockerType state;
|
||||
|
||||
// How many children were spawned during last split?
|
||||
// Is not reset on remerging the triangle.
|
||||
|
@ -133,7 +133,7 @@ protected:
|
|||
float m_old_cursor_radius;
|
||||
|
||||
// Private functions:
|
||||
bool select_triangle(int facet_idx, FacetSupportType type,
|
||||
bool select_triangle(int facet_idx, EnforcerBlockerType type,
|
||||
bool recursive_call = false);
|
||||
bool is_point_inside_cursor(const Vec3f& point) const;
|
||||
int vertices_inside(int facet_idx) const;
|
||||
|
@ -144,7 +144,7 @@ protected:
|
|||
bool is_pointer_in_triangle(int facet_idx) const;
|
||||
bool is_edge_inside_cursor(int facet_idx) const;
|
||||
void push_triangle(int a, int b, int c);
|
||||
void perform_split(int facet_idx, FacetSupportType old_state);
|
||||
void perform_split(int facet_idx, EnforcerBlockerType old_state);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -110,19 +110,20 @@ std::string header_slic3r_generated();
|
|||
// getpid platform wrapper
|
||||
extern unsigned get_current_pid();
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
template <typename Real>
|
||||
Real round_nearest(Real value, unsigned int decimals)
|
||||
{
|
||||
Real res = (Real)0;
|
||||
if (decimals == 0)
|
||||
res = ::round(value);
|
||||
else
|
||||
{
|
||||
else {
|
||||
Real power = ::pow((Real)10, (int)decimals);
|
||||
res = ::round(value * power + (Real)0.5) / power;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
// Compute the next highest power of 2 of 32-bit v
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html
|
||||
|
@ -337,6 +338,25 @@ inline std::string get_time_dhms(float time_in_secs)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
inline std::string get_time_dhm(float time_in_secs)
|
||||
{
|
||||
int days = (int)(time_in_secs / 86400.0f);
|
||||
time_in_secs -= (float)days * 86400.0f;
|
||||
int hours = (int)(time_in_secs / 3600.0f);
|
||||
time_in_secs -= (float)hours * 3600.0f;
|
||||
int minutes = (int)(time_in_secs / 60.0f);
|
||||
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
|
||||
else if (hours > 0)
|
||||
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#if WIN32
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue