Merge remote-tracking branch 'origin/master' into ys_unsaved_changes

This commit is contained in:
YuSanka 2020-09-02 09:15:11 +02:00
commit 4d053cc4ee
148 changed files with 13789 additions and 3328 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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_ */

View file

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

View file

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

View file

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

View file

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

View file

@ -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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View file

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

View file

@ -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_ */

View file

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

View file

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

View file

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

View file

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

View file

@ -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_ */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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