This commit is contained in:
bubnikv 2019-06-20 16:15:26 +02:00
commit 27459a9072
1145 changed files with 2435 additions and 1497 deletions

View file

@ -130,13 +130,10 @@ add_library(libslic3r STATIC
Print.hpp
PrintBase.cpp
PrintBase.hpp
PrintExport.hpp
PrintConfig.cpp
PrintConfig.hpp
PrintObject.cpp
PrintRegion.cpp
Rasterizer/Rasterizer.hpp
Rasterizer/Rasterizer.cpp
SLAPrint.cpp
SLAPrint.hpp
SLA/SLAAutoSupports.hpp
@ -163,6 +160,8 @@ add_library(libslic3r STATIC
MTUtils.hpp
Zipper.hpp
Zipper.cpp
MinAreaBoundingBox.hpp
MinAreaBoundingBox.cpp
miniz_extension.hpp
miniz_extension.cpp
SLA/SLABoilerPlate.hpp
@ -175,6 +174,10 @@ add_library(libslic3r STATIC
SLA/SLARotfinder.cpp
SLA/SLABoostAdapter.hpp
SLA/SLASpatIndex.hpp
SLA/SLARaster.hpp
SLA/SLARaster.cpp
SLA/SLARasterWriter.hpp
SLA/SLARasterWriter.cpp
)
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
@ -186,6 +189,7 @@ target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNE
target_link_libraries(libslic3r
libnest2d
admesh
libigl
miniz
boost_libs
clipper

View file

@ -37,6 +37,8 @@
* *
*******************************************************************************/
#ifndef SLIC3R_INT128_HPP
#define SLIC3R_INT128_HPP
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
@ -48,6 +50,8 @@
#endif
#include <cassert>
#include <cstdint>
#include <cmath>
#if ! defined(_MSC_VER) && defined(__SIZEOF_INT128__)
#define HAS_INTRINSIC_128_TYPE
@ -293,3 +297,5 @@ public:
return sign_determinant_2x2(p1, q1, p2, q2) * invert;
}
};
#endif // SLIC3R_INT128_HPP

View file

@ -5,6 +5,7 @@
#include <mutex> // for std::lock_guard
#include <functional> // for std::function
#include <utility> // for std::forward
#include <algorithm>
namespace Slic3r {
@ -182,6 +183,14 @@ public:
inline bool empty() const { return size() == 0; }
};
template<class C> bool all_of(const C &container) {
return std::all_of(container.begin(),
container.end(),
[](const typename C::value_type &v) {
return static_cast<bool>(v);
});
}
}
#endif // MTUTILS_HPP

View file

@ -0,0 +1,142 @@
#include "MinAreaBoundingBox.hpp"
#include <libslic3r/ExPolygon.hpp>
#include <boost/rational.hpp>
#include <libslic3r/Int128.hpp>
#if !defined(HAS_INTRINSIC_128_TYPE) || defined(__APPLE__)
#include <boost/multiprecision/integer.hpp>
#endif
#include <libnest2d/geometry_traits.hpp>
#include <libnest2d/utils/rotcalipers.hpp>
namespace libnest2d {
template<> struct PointType<Slic3r::Points> { using Type = Slic3r::Point; };
template<> struct CoordType<Slic3r::Point> { using Type = coord_t; };
template<> struct ShapeTag<Slic3r::ExPolygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::Polygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::Points> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Point> { using Type = PointTag; };
template<> struct ContourType<Slic3r::ExPolygon> { using Type = Slic3r::Points; };
template<> struct ContourType<Slic3r::Polygon> { using Type = Slic3r::Points; };
namespace pointlike {
template<> inline coord_t x(const Slic3r::Point& p) { return p.x(); }
template<> inline coord_t y(const Slic3r::Point& p) { return p.y(); }
template<> inline coord_t& x(Slic3r::Point& p) { return p.x(); }
template<> inline coord_t& y(Slic3r::Point& p) { return p.y(); }
} // pointlike
namespace shapelike {
template<> inline Slic3r::Points& contour(Slic3r::ExPolygon& sh) { return sh.contour.points; }
template<> inline const Slic3r::Points& contour(const Slic3r::ExPolygon& sh) { return sh.contour.points; }
template<> inline Slic3r::Points& contour(Slic3r::Polygon& sh) { return sh.points; }
template<> inline const Slic3r::Points& contour(const Slic3r::Polygon& sh) { return sh.points; }
template<> Slic3r::Points::iterator begin(Slic3r::Points& pts, const PathTag&) { return pts.begin();}
template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.begin(); }
template<> Slic3r::Points::iterator end(Slic3r::Points& pts, const PathTag&) { return pts.end();}
template<> Slic3r::Points::const_iterator cend(const Slic3r::Points& pts, const PathTag&) { return pts.cend(); }
template<> inline Slic3r::ExPolygon create<Slic3r::ExPolygon>(Slic3r::Points&& contour)
{
Slic3r::ExPolygon expoly; expoly.contour.points.swap(contour);
return expoly;
}
template<> inline Slic3r::Polygon create<Slic3r::Polygon>(Slic3r::Points&& contour)
{
Slic3r::Polygon poly; poly.points.swap(contour);
return poly;
}
} // shapelike
} // libnest2d
namespace Slic3r {
// Used as compute type.
using Unit = int64_t;
#if !defined(HAS_INTRINSIC_128_TYPE) || defined(__APPLE__)
using Rational = boost::rational<boost::multiprecision::int128_t>;
#else
using Rational = boost::rational<__int128>;
#endif
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
{
const Polygon& chull = pc == pcConvex ? p : libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<Polygon, Unit, Rational>(chull);
m_right = box.right_extent();
m_bottom = box.bottom_extent();
m_axis = box.axis();
}
MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
{
const ExPolygon& chull = pc == pcConvex ? p : libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<ExPolygon, Unit, Rational>(chull);
m_right = box.right_extent();
m_bottom = box.bottom_extent();
m_axis = box.axis();
}
MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc)
{
const Points& chull = pc == pcConvex ? pts : libnest2d::sl::convexHull(pts);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<Points, Unit, Rational>(chull);
m_right = box.right_extent();
m_bottom = box.bottom_extent();
m_axis = box.axis();
}
double MinAreaBoundigBox::angle_to_X() const
{
double ret = std::atan2(m_axis.y(), m_axis.x());
auto s = std::signbit(ret);
if(s) ret += 2 * PI;
return -ret;
}
long double MinAreaBoundigBox::width() const
{
return std::abs(m_bottom) / std::sqrt(libnest2d::pl::magnsq<Point, long double>(m_axis));
}
long double MinAreaBoundigBox::height() const
{
return std::abs(m_right) / std::sqrt(libnest2d::pl::magnsq<Point, long double>(m_axis));
}
long double MinAreaBoundigBox::area() const
{
long double asq = libnest2d::pl::magnsq<Point, long double>(m_axis);
return m_bottom * m_right / asq;
}
void remove_collinear_points(Polygon &p)
{
p = libnest2d::removeCollinearPoints<Polygon>(p, Unit(0));
}
void remove_collinear_points(ExPolygon &p)
{
p = libnest2d::removeCollinearPoints<ExPolygon>(p, Unit(0));
}
}

View file

@ -0,0 +1,59 @@
#ifndef MINAREABOUNDINGBOX_HPP
#define MINAREABOUNDINGBOX_HPP
#include "libslic3r/Point.hpp"
namespace Slic3r {
class Polygon;
class ExPolygon;
void remove_collinear_points(Polygon& p);
void remove_collinear_points(ExPolygon& p);
/// A class that holds a rotated bounding box. If instantiated with a polygon
/// type it will hold the minimum area bounding box for the given polygon.
/// If the input polygon is convex, the complexity is linear to the number of
/// points. Otherwise a convex hull of O(n*log(n)) has to be performed.
class MinAreaBoundigBox {
Point m_axis;
long double m_bottom = 0.0l, m_right = 0.0l;
public:
// Polygons can be convex or simple (convex or concave with possible holes)
enum PolygonLevel {
pcConvex, pcSimple
};
// Constructors with various types of geometry data used in Slic3r.
// If the convexity is known apriory, pcConvex can be used to skip
// convex hull calculation. It is very important that the input polygons
// do NOT have any collinear points (except for the first and the last
// vertex being the same -- meaning a closed polygon for boost)
// To make sure this constraint is satisfied, you can call
// remove_collinear_points on the input polygon before handing over here)
explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple);
explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple);
explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple);
// Returns the angle in radians needed for the box to be aligned with the
// X axis. Rotate the polygon by this angle and it will be aligned.
double angle_to_X() const;
// The box width
long double width() const;
// The box height
long double height() const;
// The box area
long double area() const;
// The axis of the rotated box. If the angle_to_X is not sufficiently
// precise, use this unnormalized direction vector.
const Point& axis() const { return m_axis; }
};
}
#endif // MINAREABOUNDINGBOX_HPP

View file

@ -9,6 +9,31 @@
#include <ClipperUtils.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/multiprecision/integer.hpp>
#include <boost/rational.hpp>
namespace libnest2d {
#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__)
using LargeInt = __int128;
#else
using LargeInt = boost::multiprecision::int128_t;
template<> struct _NumTag<LargeInt> { using Type = ScalarTag; };
#endif
template<class T> struct _NumTag<boost::rational<T>> { using Type = RationalTag; };
namespace nfp {
template<class S>
struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
{
NfpResult<S> operator()(const S &sh, const S &other)
{
return nfpConvexOnly<S, boost::rational<LargeInt>>(sh, other);
}
};
}
}
namespace Slic3r {
@ -130,7 +155,7 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) {
// at the same time, it has to provide reasonable results.
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const PointImpl& bincenter,
const shapelike::Shapes<PolygonImpl>& merged_pile,
const TMultiShape<PolygonImpl>& merged_pile,
const Box& pilebb,
const ItemGroup& items,
const Item &item,
@ -293,7 +318,7 @@ class AutoArranger {};
// management and spatial index structures for acceleration.
template<class TBin>
class _ArrBase {
protected:
public:
// Useful type shortcuts...
using Placer = TPacker<TBin>;
@ -301,7 +326,9 @@ protected:
using Packer = Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig;
using Distance = TCoord<PointImpl>;
using Pile = sl::Shapes<PolygonImpl>;
using Pile = TMultiShape<PolygonImpl>;
protected:
Packer m_pck;
PConfig m_pconf; // Placement configuration
@ -539,7 +566,10 @@ public:
// 2D shape from top view.
using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo& wti) {
ShapeData2D projectModelFromTop(const Slic3r::Model &model,
const WipeTowerInfo &wti,
double tolerance)
{
ShapeData2D ret;
// Count all the items on the bin (all the object's instances)
@ -561,21 +591,32 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo&
// Object instances should carry the same scaling and
// x, y rotation that is why we use the first instance.
{
ModelInstance *finst = objptr->instances.front();
Vec3d rotation = finst->get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror());
ModelInstance *finst = objptr->instances.front();
Vec3d rotation = finst->get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(
Vec3d::Zero(),
rotation,
finst->get_scaling_factor(),
finst->get_mirror());
Polygon p = objptr->convex_hull_2d(trafo_instance);
assert(! p.points.empty());
// this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209
if (p.points.empty())
continue;
assert(!p.points.empty());
// this may happen for malformed models, see:
// https://github.com/prusa3d/PrusaSlicer/issues/2209
if (p.points.empty()) continue;
if(tolerance > EPSILON) {
Polygons pp { p };
pp = p.simplify(double(scaled(tolerance)));
if (!pp.empty()) p = pp.front();
}
p.reverse();
assert(!p.is_counter_clockwise());
p.append(p.first_point());
clpath = Slic3rMultiPoint_to_ClipperPath(p);
auto firstp = clpath.front(); clpath.emplace_back(firstp);
}
Vec3d rotation0 = objptr->instances.front()->get_rotation();
@ -589,7 +630,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo&
// Invalid geometries would throw exceptions when arranging
if(item.vertexCount() > 3) {
item.rotation(float(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()))),
item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()));
item.translation({
ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR),
ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR)
@ -741,6 +782,8 @@ BedShapeHint bedShape(const Polyline &bed) {
return ret;
}
static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1;
// The final client function to arrange the Model. A progress indicator and
// a stop predicate can be also be passed to control the process.
bool arrange(Model &model, // The model with the geometries
@ -755,9 +798,9 @@ bool arrange(Model &model, // The model with the geometries
std::function<bool ()> stopcondition)
{
bool ret = true;
// Get the 2D projected shapes with their 3D model instance pointers
auto shapemap = arr::projectModelFromTop(model, wti);
auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM);
// Copy the references for the shapes only as the arranger expects a
// sequence of objects convertible to Item or ClipperPolygon
@ -782,7 +825,7 @@ bool arrange(Model &model, // The model with the geometries
static_cast<libnest2d::Coord>(bbb.min(0)),
static_cast<libnest2d::Coord>(bbb.min(1))
},
{
{
static_cast<libnest2d::Coord>(bbb.max(0)),
static_cast<libnest2d::Coord>(bbb.max(1))
});
@ -856,9 +899,9 @@ void find_new_position(const Model &model,
coord_t min_obj_distance,
const Polyline &bed,
WipeTowerInfo& wti)
{
{
// Get the 2D projected shapes with their 3D model instance pointers
auto shapemap = arr::projectModelFromTop(model, wti);
auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM);
// Copy the references for the shapes only as the arranger expects a
// sequence of objects convertible to Item or ClipperPolygon

View file

@ -2258,6 +2258,20 @@ void PrintConfigDef::init_sla_params()
def->min = 100;
def->set_default_value(new ConfigOptionInt(1440));
def = this->add("display_mirror_x", coBool);
def->full_label = L("Display horizontal mirroring");
def->label = L("Mirror horizontally");
def->tooltip = L("Enable horizontal mirroring of output images");
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("display_mirror_y", coBool);
def->full_label = L("Display vertical mirroring");
def->label = L("Mirror vertically");
def->tooltip = L("Enable vertical mirroring of output images");
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("display_orientation", coEnum);
def->label = L("Display orientation");
def->tooltip = L("Set the actual LCD display orientation inside the SLA printer."

View file

@ -1083,6 +1083,8 @@ public:
ConfigOptionInt display_pixels_x;
ConfigOptionInt display_pixels_y;
ConfigOptionEnum<SLADisplayOrientation> display_orientation;
ConfigOptionBool display_mirror_x;
ConfigOptionBool display_mirror_y;
ConfigOptionFloats relative_correction;
ConfigOptionFloat absolute_correction;
ConfigOptionFloat gamma_correction;
@ -1099,6 +1101,8 @@ protected:
OPT_PTR(display_height);
OPT_PTR(display_pixels_x);
OPT_PTR(display_pixels_y);
OPT_PTR(display_mirror_x);
OPT_PTR(display_mirror_y);
OPT_PTR(display_orientation);
OPT_PTR(relative_correction);
OPT_PTR(absolute_correction);

View file

@ -1,327 +0,0 @@
#ifndef PRINTEXPORT_HPP
#define PRINTEXPORT_HPP
// For png export of the sliced model
#include <fstream>
#include <sstream>
#include <vector>
#include <boost/log/trivial.hpp>
#include <boost/filesystem/path.hpp>
#include "Rasterizer/Rasterizer.hpp"
//#include <tbb/parallel_for.h>
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
namespace Slic3r {
// Used for addressing parameters of FilePrinter::set_statistics()
enum ePrintStatistics
{
psUsedMaterial = 0,
psNumFade,
psNumSlow,
psNumFast,
psCnt
};
enum class FilePrinterFormat {
SLA_PNGZIP,
SVG
};
/*
* Interface for a file printer of the slices. Implementation can be an SVG
* or PNG printer or any other format.
*
* The format argument specifies the output format of the printer and it enables
* different implementations of this class template for each supported format.
*
*/
template<FilePrinterFormat format>
class FilePrinter {
public:
// Draw a polygon which is a polygon inside a slice on the specified layer.
void draw_polygon(const ExPolygon& p, unsigned lyr);
void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr);
// Tell the printer how many layers should it consider.
void layers(unsigned layernum);
// Get the number of layers in the print.
unsigned layers() const;
/* Switch to a particular layer. If there where less layers then the
* specified layer number than an appropriate number of layers will be
* allocated in the printer.
*/
void begin_layer(unsigned layer);
// Allocate a new layer on top of the last and switch to it.
void begin_layer();
/*
* Finish the selected layer. It means that no drawing is allowed on that
* layer anymore. This fact can be used to prepare the file system output
* data like png comprimation and so on.
*/
void finish_layer(unsigned layer);
// Finish the top layer.
void finish_layer();
// Save all the layers into the file (or dir) specified in the path argument
// An optional project name can be added to be used for the layer file names
void save(const std::string& path, const std::string& projectname = "");
// Save only the selected layer to the file specified in path argument.
void save_layer(unsigned lyr, const std::string& path);
};
// Provokes static_assert in the right way.
template<class T = void> struct VeryFalse { static const bool value = false; };
// This can be explicitly implemented in the gui layer or the default Zipper
// API in libslic3r with minz.
template<class Fmt> class LayerWriter {
public:
LayerWriter(const std::string& /*zipfile_path*/)
{
static_assert(VeryFalse<Fmt>::value,
"No layer writer implementation provided!");
}
// Should create a new file within the zip with the given filename. It
// should also finish any previous entry.
void next_entry(const std::string& /*fname*/) {}
// Should create a new file within the archive and write the provided data.
void binary_entry(const std::string& /*fname*/,
const std::uint8_t* buf, size_t len);
// Test whether the object can still be used for writing.
bool is_ok() { return false; }
// Write some data (text) into the current file (entry) within the archive.
template<class T> LayerWriter& operator<<(T&& /*arg*/) {
return *this;
}
// Flush the current entry into the archive.
void finalize() {}
};
// Implementation for PNG raster output
// Be aware that if a large number of layers are allocated, it can very well
// exhaust the available memory especially on 32 bit platform.
template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
{
struct Layer {
Raster raster;
RawBytes rawbytes;
Layer() {}
Layer(const Layer&) = delete;
Layer(Layer&& m):
raster(std::move(m.raster)) {}
};
// We will save the compressed PNG data into stringstreams which can be done
// in parallel. Later we can write every layer to the disk sequentially.
std::vector<Layer> m_layers_rst;
Raster::Resolution m_res;
Raster::PixelDim m_pxdim;
double m_exp_time_s = .0, m_exp_time_first_s = .0;
double m_layer_height = .0;
Raster::Origin m_o = Raster::Origin::TOP_LEFT;
double m_gamma;
double m_used_material = 0.0;
int m_cnt_fade_layers = 0;
int m_cnt_slow_layers = 0;
int m_cnt_fast_layers = 0;
std::string createIniContent(const std::string& projectname) {
using std::string;
using std::to_string;
auto expt_str = to_string(m_exp_time_s);
auto expt_first_str = to_string(m_exp_time_first_s);
auto layerh_str = to_string(m_layer_height);
const std::string cnt_fade_layers = to_string(m_cnt_fade_layers);
const std::string cnt_slow_layers = to_string(m_cnt_slow_layers);
const std::string cnt_fast_layers = to_string(m_cnt_fast_layers);
const std::string used_material = to_string(m_used_material);
return string(
"action = print\n"
"jobDir = ") + projectname + "\n" +
"expTime = " + expt_str + "\n"
"expTimeFirst = " + expt_first_str + "\n"
"numFade = " + cnt_fade_layers + "\n"
"layerHeight = " + layerh_str + "\n"
"usedMaterial = " + used_material + "\n"
"numSlow = " + cnt_slow_layers + "\n"
"numFast = " + cnt_fast_layers + "\n";
}
public:
enum RasterOrientation {
RO_LANDSCAPE,
RO_PORTRAIT
};
// We will play with the raster's coordinate origin parameter. When the
// printer should print in landscape mode it should have the Y axis flipped
// because the layers should be displayed upside down. PNG has its
// coordinate origin in the top-left corner so normally the Raster objects
// should be instantiated with the TOP_LEFT flag. However, in landscape mode
// we do want the pictures to be upside down so we will make BOTTOM_LEFT
// type rasters and the PNG format will do the flipping automatically.
// In case of portrait images, we have to rotate the image by a 90 degrees
// and flip the y axis. To get the correct upside-down orientation of the
// slice images, we can flip the x and y coordinates of the input polygons
// and do the Y flipping of the image. This will generate the correct
// orientation in portrait mode.
inline FilePrinter(double width_mm, double height_mm,
unsigned width_px, unsigned height_px,
double layer_height,
double exp_time, double exp_time_first,
RasterOrientation ro = RO_PORTRAIT,
double gamma = 1.0):
m_res(width_px, height_px),
m_pxdim(width_mm/width_px, height_mm/height_px),
m_exp_time_s(exp_time),
m_exp_time_first_s(exp_time_first),
m_layer_height(layer_height),
// Here is the trick with the orientation.
m_o(ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT :
Raster::Origin::TOP_LEFT ),
m_gamma(gamma)
{
}
FilePrinter(const FilePrinter& ) = delete;
FilePrinter(FilePrinter&& m):
m_layers_rst(std::move(m.m_layers_rst)),
m_res(m.m_res),
m_pxdim(m.m_pxdim) {}
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
m_layers_rst[lyr].raster.draw(p);
}
inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
m_layers_rst[lyr].raster.draw(p);
}
inline void begin_layer(unsigned lyr) {
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_o, m_gamma);
}
inline void begin_layer() {
m_layers_rst.emplace_back();
m_layers_rst.front().raster.reset(m_res, m_pxdim, m_o, m_gamma);
}
inline void finish_layer(unsigned lyr_id) {
assert(lyr_id < m_layers_rst.size());
m_layers_rst[lyr_id].rawbytes =
m_layers_rst[lyr_id].raster.save(Raster::Compression::PNG);
m_layers_rst[lyr_id].raster.reset();
}
inline void finish_layer() {
if(!m_layers_rst.empty()) {
m_layers_rst.back().rawbytes =
m_layers_rst.back().raster.save(Raster::Compression::PNG);
m_layers_rst.back().raster.reset();
}
}
template<class LyrFmt>
inline void save(const std::string& fpath, const std::string& prjname = "")
{
try {
LayerWriter<LyrFmt> writer(fpath);
if(!writer.is_ok()) return;
std::string project = prjname.empty()?
boost::filesystem::path(fpath).stem().string() : prjname;
writer.next_entry("config.ini");
if(!writer.is_ok()) return;
writer << createIniContent(project);
for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++)
{
if(m_layers_rst[i].rawbytes.size() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
if(!writer.is_ok()) break;
writer.binary_entry(zfilename,
m_layers_rst[i].rawbytes.data(),
m_layers_rst[i].rawbytes.size());
}
}
writer.finalize();
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
// Rethrow the exception
throw;
}
}
void save_layer(unsigned lyr, const std::string& path) {
unsigned i = lyr;
assert(i < m_layers_rst.size());
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", lyr);
std::string loc = path + "layer" + lyrnum + ".png";
std::fstream out(loc, std::fstream::out | std::fstream::binary);
if(out.good()) {
m_layers_rst[i].raster.save(out, Raster::Compression::PNG);
} else {
BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
}
out.close();
m_layers_rst[i].raster.reset();
}
void set_statistics(const std::vector<double> statistics)
{
if (statistics.size() != psCnt)
return;
m_used_material = statistics[psUsedMaterial];
m_cnt_fade_layers = int(statistics[psNumFade]);
m_cnt_slow_layers = int(statistics[psNumSlow]);
m_cnt_fast_layers = int(statistics[psNumFast]);
}
};
}
#endif // PRINTEXPORT_HPP

View file

@ -1,5 +1,10 @@
#include "Rasterizer.hpp"
#include <ExPolygon.hpp>
#ifndef SLARASTER_CPP
#define SLARASTER_CPP
#include <functional>
#include "SLARaster.hpp"
#include "libslic3r/ExPolygon.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
// For rasterizing
@ -19,11 +24,13 @@
namespace Slic3r {
const Polygon& contour(const ExPolygon& p) { return p.contour; }
const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
inline const Polygon& contour(const ExPolygon& p) { return p.contour; }
inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
const Polygons& holes(const ExPolygon& p) { return p.holes; }
const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
inline const Polygons& holes(const ExPolygon& p) { return p.holes; }
inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
namespace sla {
class Raster::Impl {
public:
@ -39,7 +46,7 @@ public:
static const TPixel ColorWhite;
static const TPixel ColorBlack;
using Origin = Raster::Origin;
using Format = Raster::Format;
private:
Raster::Resolution m_resolution;
@ -52,16 +59,21 @@ private:
TRendererAA m_renderer;
std::function<double(double)> m_gammafn;
Origin m_o;
std::array<bool, 2> m_mirror;
Format m_fmt = Format::PNG;
inline void flipy(agg::path_storage& path) const {
path.flip_y(0, m_resolution.height_px);
}
inline void flipx(agg::path_storage& path) const {
path.flip_x(0, m_resolution.width_px);
}
public:
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
Origin o, double gamma = 1.0):
const std::array<bool, 2>& mirror, double gamma = 1.0):
m_resolution(res),
// m_pxdim(pd),
m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm),
@ -72,7 +84,7 @@ public:
m_pixfmt(m_rbuf),
m_raw_renderer(m_pixfmt),
m_renderer(m_raw_renderer),
m_o(o)
m_mirror(mirror)
{
m_renderer.color(ColorWhite);
@ -81,6 +93,19 @@ public:
clear();
}
inline Impl(const Raster::Resolution& res,
const Raster::PixelDim &pd,
Format fmt,
double gamma = 1.0):
Impl(res, pd, {false, false}, gamma)
{
switch (fmt) {
case Format::PNG: m_mirror = {false, true}; break;
case Format::RAW: m_mirror = {false, false}; break;
}
m_fmt = fmt;
}
template<class P> void draw(const P &poly) {
agg::rasterizer_scanline_aa<> ras;
@ -89,14 +114,16 @@ public:
ras.gamma(m_gammafn);
auto&& path = to_path(contour(poly));
if(m_o == Origin::TOP_LEFT) flipy(path);
if(m_mirror[X]) flipx(path);
if(m_mirror[Y]) flipy(path);
ras.add_path(path);
for(auto& h : holes(poly)) {
auto&& holepath = to_path(h);
if(m_o == Origin::TOP_LEFT) flipy(holepath);
if(m_mirror[X]) flipx(holepath);
if(m_mirror[Y]) flipy(holepath);
ras.add_path(holepath);
}
@ -108,11 +135,11 @@ public:
}
inline TBuffer& buffer() { return m_buf; }
inline Format format() const { return m_fmt; }
inline const Raster::Resolution resolution() { return m_resolution; }
inline Origin origin() const /*noexcept*/ { return m_o; }
private:
inline double getPx(const Point& p) {
return p(0) * m_pxdim_scaled.w_mm;
@ -154,30 +181,30 @@ private:
const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o, double g):
m_impl(new Impl(r, pd, o, g)) {}
template<> Raster::Raster() { reset(); };
Raster::~Raster() = default;
Raster::Raster() {}
// Raster::Raster(Raster &&m) = default;
// Raster& Raster::operator=(Raster&&) = default;
Raster::~Raster() {}
Raster::Raster(Raster &&m):
m_impl(std::move(m.m_impl)) {}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
double g)
{
// Free up the unnecessary memory and make sure it stays clear after
// an exception
auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT;
reset(r, pd, o, g);
// FIXME: remove after migrating to higher version of windows compiler
Raster::Raster(Raster &&m): m_impl(std::move(m.m_impl)) {}
Raster& Raster::operator=(Raster &&m) {
m_impl = std::move(m.m_impl); return *this;
}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
Raster::Origin o, double gamma)
Format fmt, double gamma)
{
m_impl.reset();
m_impl.reset(new Impl(r, pd, o, gamma));
m_impl.reset(new Impl(r, pd, fmt, gamma));
}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
const std::array<bool, 2>& mirror, double gamma)
{
m_impl.reset();
m_impl.reset(new Impl(r, pd, mirror, gamma));
}
void Raster::reset()
@ -208,13 +235,13 @@ void Raster::draw(const ClipperLib::Polygon &poly)
m_impl->draw(poly);
}
void Raster::save(std::ostream& stream, Compression comp)
void Raster::save(std::ostream& stream, Format fmt)
{
assert(m_impl);
if(!stream.good()) return;
switch(comp) {
case Compression::PNG: {
switch(fmt) {
case Format::PNG: {
auto& b = m_impl->buffer();
size_t out_len = 0;
void * rawdata = tdefl_write_image_to_png_file_in_memory(
@ -231,7 +258,7 @@ void Raster::save(std::ostream& stream, Compression comp)
break;
}
case Compression::RAW: {
case Format::RAW: {
stream << "P5 "
<< m_impl->resolution().width_px << " "
<< m_impl->resolution().height_px << " "
@ -244,14 +271,19 @@ void Raster::save(std::ostream& stream, Compression comp)
}
}
RawBytes Raster::save(Raster::Compression comp)
void Raster::save(std::ostream &stream)
{
save(stream, m_impl->format());
}
RawBytes Raster::save(Format fmt)
{
assert(m_impl);
std::vector<std::uint8_t> data; size_t s = 0;
switch(comp) {
case Compression::PNG: {
switch(fmt) {
case Format::PNG: {
void *rawdata = tdefl_write_image_to_png_file_in_memory(
m_impl->buffer().data(),
int(resolution().width_px),
@ -265,7 +297,7 @@ RawBytes Raster::save(Raster::Compression comp)
MZ_FREE(rawdata);
break;
}
case Compression::RAW: {
case Format::RAW: {
auto header = std::string("P5 ") +
std::to_string(m_impl->resolution().width_px) + " " +
std::to_string(m_impl->resolution().height_px) + " " + "255 ";
@ -286,4 +318,12 @@ RawBytes Raster::save(Raster::Compression comp)
return {std::move(data)};
}
RawBytes Raster::save()
{
return save(m_impl->format());
}
}
}
#endif // SLARASTER_CPP

View file

@ -1,17 +1,21 @@
#ifndef RASTERIZER_HPP
#define RASTERIZER_HPP
#ifndef SLARASTER_HPP
#define SLARASTER_HPP
#include <ostream>
#include <memory>
#include <vector>
#include <array>
#include <utility>
#include <cstdint>
namespace ClipperLib { struct Polygon; }
namespace Slic3r {
namespace Slic3r {
class ExPolygon;
namespace sla {
// Raw byte buffer paired with its size. Suitable for compressed PNG data.
class RawBytes {
@ -23,15 +27,18 @@ public:
size_t size() const { return m_buffer.size(); }
const uint8_t * data() { return m_buffer.data(); }
RawBytes(const RawBytes&) = delete;
RawBytes& operator=(const RawBytes&) = delete;
// /////////////////////////////////////////////////////////////////////////
// FIXME: the following is needed for MSVC2013 compatibility
// /////////////////////////////////////////////////////////////////////////
RawBytes(const RawBytes&) = delete;
RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {}
// RawBytes(RawBytes&&) = default;
// RawBytes& operator=(RawBytes&&) = default;
RawBytes& operator=(const RawBytes&) = delete;
RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {}
RawBytes& operator=(RawBytes&& mv) {
m_buffer = std::move(mv.m_buffer);
return *this;
@ -54,28 +61,19 @@ class Raster {
public:
/// Supported compression types
enum class Compression {
enum class Format {
RAW, //!> Uncompressed pixel data
PNG //!> PNG compression
};
/// The Rasterizer expects the input polygons to have their coordinate
/// system origin in the bottom left corner. If the raster is then
/// configured with the TOP_LEFT origin parameter (in the constructor) than
/// it will flip the Y axis in output to maintain the correct orientation.
/// This is the default case with PNG images. They have the origin in the
/// top left corner. Without the flipping, the image would be upside down
/// with the scaled (clipper) coordinate system of the input polygons.
enum class Origin {
TOP_LEFT,
BOTTOM_LEFT
};
/// Type that represents a resolution in pixels.
struct Resolution {
unsigned width_px;
unsigned height_px;
inline Resolution(unsigned w, unsigned h): width_px(w), height_px(h) {}
inline Resolution(unsigned w = 0, unsigned h = 0):
width_px(w), height_px(h) {}
inline unsigned pixels() const /*noexcept*/ {
return width_px * height_px;
}
@ -85,24 +83,34 @@ public:
struct PixelDim {
double w_mm;
double h_mm;
inline PixelDim(double px_width_mm, double px_height_mm ):
inline PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0):
w_mm(px_width_mm), h_mm(px_height_mm) {}
};
/// Constructor taking the resolution and the pixel dimension.
Raster(const Resolution& r, const PixelDim& pd,
Origin o = Origin::BOTTOM_LEFT, double gamma = 1.0);
template <class...Args> Raster(Args...args) {
reset(std::forward<Args>(args)...);
}
Raster();
Raster(const Raster& cpy) = delete;
Raster& operator=(const Raster& cpy) = delete;
Raster(Raster&& m);
Raster& operator=(Raster&&);
~Raster();
/// Reallocated everything for the given resolution and pixel dimension.
void reset(const Resolution& r, const PixelDim& pd, double gamma = 1.0);
void reset(const Resolution& r, const PixelDim& pd, Origin o, double gamma);
/// The third parameter is either the X, Y mirroring or a supported format
/// for which the correct mirroring will be configured.
void reset(const Resolution&,
const PixelDim&,
const std::array<bool, 2>& mirror,
double gamma = 1.0);
void reset(const Resolution& r,
const PixelDim& pd,
Format o,
double gamma = 1.0);
/**
* Release the allocated resources. Drawing in this state ends in
* unspecified behavior.
@ -119,11 +127,24 @@ public:
void draw(const ExPolygon& poly);
void draw(const ClipperLib::Polygon& poly);
// Saving the raster:
// It is possible to override the format given in the constructor but
// be aware that the mirroring will not be modified.
/// Save the raster on the specified stream.
void save(std::ostream& stream, Compression comp = Compression::RAW);
void save(std::ostream& stream, Format);
void save(std::ostream& stream);
RawBytes save(Compression comp = Compression::RAW);
/// Save into a continuous byte stream which is returned.
RawBytes save(Format fmt);
RawBytes save();
};
}
#endif // RASTERIZER_HPP
// This prevents the duplicate default constructor warning on MSVC2013
template<> Raster::Raster();
} // sla
} // Slic3r
#endif // SLARASTER_HPP

View file

@ -0,0 +1,136 @@
#include "SLARasterWriter.hpp"
#include "libslic3r/Zipper.hpp"
#include "ExPolygon.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
#include <boost/log/trivial.hpp>
#include <boost/filesystem/path.hpp>
namespace Slic3r { namespace sla {
std::string SLARasterWriter::createIniContent(const std::string& projectname) const
{
auto expt_str = std::to_string(m_exp_time_s);
auto expt_first_str = std::to_string(m_exp_time_first_s);
auto layerh_str = std::to_string(m_layer_height);
const std::string cnt_fade_layers = std::to_string(m_cnt_fade_layers);
const std::string cnt_slow_layers = std::to_string(m_cnt_slow_layers);
const std::string cnt_fast_layers = std::to_string(m_cnt_fast_layers);
const std::string used_material = std::to_string(m_used_material);
return std::string(
"action = print\n"
"jobDir = ") + projectname + "\n" +
"expTime = " + expt_str + "\n"
"expTimeFirst = " + expt_first_str + "\n"
"numFade = " + cnt_fade_layers + "\n"
"layerHeight = " + layerh_str + "\n"
"usedMaterial = " + used_material + "\n"
"numSlow = " + cnt_slow_layers + "\n"
"numFast = " + cnt_fast_layers + "\n";
}
void SLARasterWriter::flpXY(ClipperLib::Polygon &poly)
{
for(auto& p : poly.Contour) std::swap(p.X, p.Y);
std::reverse(poly.Contour.begin(), poly.Contour.end());
for(auto& h : poly.Holes) {
for(auto& p : h) std::swap(p.X, p.Y);
std::reverse(h.begin(), h.end());
}
}
void SLARasterWriter::flpXY(ExPolygon &poly)
{
for(auto& p : poly.contour.points) p = Point(p.y(), p.x());
std::reverse(poly.contour.points.begin(), poly.contour.points.end());
for(auto& h : poly.holes) {
for(auto& p : h.points) p = Point(p.y(), p.x());
std::reverse(h.points.begin(), h.points.end());
}
}
SLARasterWriter::SLARasterWriter(const SLAPrinterConfig &cfg,
const SLAMaterialConfig &mcfg,
double layer_height)
{
double w = cfg.display_width.getFloat();
double h = cfg.display_height.getFloat();
auto pw = unsigned(cfg.display_pixels_x.getInt());
auto ph = unsigned(cfg.display_pixels_y.getInt());
m_mirror[X] = cfg.display_mirror_x.getBool();
// PNG raster will implicitly do an Y mirror
m_mirror[Y] = ! cfg.display_mirror_y.getBool();
auto ro = cfg.display_orientation.getInt();
if(ro == roPortrait) {
std::swap(w, h);
std::swap(pw, ph);
m_o = roPortrait;
// XY flipping implicitly does an X mirror
m_mirror[X] = ! m_mirror[X];
} else m_o = roLandscape;
m_res = Raster::Resolution(pw, ph);
m_pxdim = Raster::PixelDim(w/pw, h/ph);
m_exp_time_s = mcfg.exposure_time.getFloat();
m_exp_time_first_s = mcfg.initial_exposure_time.getFloat();
m_layer_height = layer_height;
m_gamma = cfg.gamma_correction.getFloat();
}
void SLARasterWriter::save(const std::string &fpath, const std::string &prjname)
{
try {
Zipper zipper(fpath); // zipper with no compression
std::string project = prjname.empty()?
boost::filesystem::path(fpath).stem().string() : prjname;
zipper.add_entry("config.ini");
zipper << createIniContent(project);
for(unsigned i = 0; i < m_layers_rst.size(); i++)
{
if(m_layers_rst[i].rawbytes.size() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
// Add binary entry to the zipper
zipper.add_entry(zfilename,
m_layers_rst[i].rawbytes.data(),
m_layers_rst[i].rawbytes.size());
}
}
zipper.finalize();
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
// Rethrow the exception
throw;
}
}
void SLARasterWriter::set_statistics(const std::vector<double> statistics)
{
if (statistics.size() != psCnt)
return;
m_used_material = statistics[psUsedMaterial];
m_cnt_fade_layers = int(statistics[psNumFade]);
m_cnt_slow_layers = int(statistics[psNumSlow]);
m_cnt_fast_layers = int(statistics[psNumFast]);
}
} // namespace sla
} // namespace Slic3r

View file

@ -0,0 +1,167 @@
#ifndef SLARASTERWRITER_HPP
#define SLARASTERWRITER_HPP
// For png export of the sliced model
#include <fstream>
#include <sstream>
#include <vector>
#include <array>
#include "libslic3r/PrintConfig.hpp"
#include "SLARaster.hpp"
namespace Slic3r { namespace sla {
// Implementation for PNG raster output
// Be aware that if a large number of layers are allocated, it can very well
// exhaust the available memory especially on 32 bit platform.
// This class is designed to be used in parallel mode. Layers have an ID and
// each layer can be written and compressed independently (in parallel).
// At the end when all layers where written, the save method can be used to
// write out the result into a zipped archive.
class SLARasterWriter
{
public:
enum RasterOrientation {
roLandscape,
roPortrait
};
// Used for addressing parameters of set_statistics()
enum ePrintStatistics
{
psUsedMaterial = 0,
psNumFade,
psNumSlow,
psNumFast,
psCnt
};
private:
// A struct to bind the raster image data and its compressed bytes together.
struct Layer {
Raster raster;
RawBytes rawbytes;
Layer() = default;
Layer(const Layer&) = delete; // The image is big, do not copy by accident
Layer& operator=(const Layer&) = delete;
// /////////////////////////////////////////////////////////////////////
// FIXME: the following is needed for MSVC2013 compatibility
// /////////////////////////////////////////////////////////////////////
// Layer(Layer&& m) = default;
// Layer& operator=(Layer&&) = default;
Layer(Layer &&m):
raster(std::move(m.raster)), rawbytes(std::move(m.rawbytes)) {}
Layer& operator=(Layer &&m) {
raster = std::move(m.raster); rawbytes = std::move(m.rawbytes);
return *this;
}
};
// We will save the compressed PNG data into RawBytes type buffers in
// parallel. Later we can write every layer to the disk sequentially.
std::vector<Layer> m_layers_rst;
Raster::Resolution m_res;
Raster::PixelDim m_pxdim;
double m_exp_time_s = .0, m_exp_time_first_s = .0;
double m_layer_height = .0;
RasterOrientation m_o = roPortrait;
std::array<bool, 2> m_mirror;
double m_gamma;
double m_used_material = 0.0;
int m_cnt_fade_layers = 0;
int m_cnt_slow_layers = 0;
int m_cnt_fast_layers = 0;
std::string createIniContent(const std::string& projectname) const;
static void flpXY(ClipperLib::Polygon& poly);
static void flpXY(ExPolygon& poly);
public:
SLARasterWriter(const SLAPrinterConfig& cfg,
const SLAMaterialConfig& mcfg,
double layer_height);
SLARasterWriter(const SLARasterWriter& ) = delete;
SLARasterWriter& operator=(const SLARasterWriter&) = delete;
// /////////////////////////////////////////////////////////////////////////
// FIXME: the following is needed for MSVC2013 compatibility
// /////////////////////////////////////////////////////////////////////////
// SLARasterWriter(SLARasterWriter&& m) = default;
// SLARasterWriter& operator=(SLARasterWriter&&) = default;
SLARasterWriter(SLARasterWriter&& m):
m_layers_rst(std::move(m.m_layers_rst)),
m_res(m.m_res),
m_pxdim(m.m_pxdim),
m_exp_time_s(m.m_exp_time_s),
m_exp_time_first_s(m.m_exp_time_first_s),
m_layer_height(m.m_layer_height),
m_o(m.m_o),
m_mirror(std::move(m.m_mirror)),
m_gamma(m.m_gamma),
m_used_material(m.m_used_material),
m_cnt_fade_layers(m.m_cnt_fade_layers),
m_cnt_slow_layers(m.m_cnt_slow_layers),
m_cnt_fast_layers(m.m_cnt_fast_layers)
{}
// /////////////////////////////////////////////////////////////////////////
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
template<class Poly> void draw_polygon(const Poly& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
if(m_o == roPortrait) {
Poly poly(p); flpXY(poly);
m_layers_rst[lyr].raster.draw(poly);
}
else m_layers_rst[lyr].raster.draw(p);
}
inline void begin_layer(unsigned lyr) {
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_mirror, m_gamma);
}
inline void begin_layer() {
m_layers_rst.emplace_back();
m_layers_rst.front().raster.reset(m_res, m_pxdim, m_mirror, m_gamma);
}
inline void finish_layer(unsigned lyr_id) {
assert(lyr_id < m_layers_rst.size());
m_layers_rst[lyr_id].rawbytes =
m_layers_rst[lyr_id].raster.save(Raster::Format::PNG);
m_layers_rst[lyr_id].raster.reset();
}
inline void finish_layer() {
if(!m_layers_rst.empty()) {
m_layers_rst.back().rawbytes =
m_layers_rst.back().raster.save(Raster::Format::PNG);
m_layers_rst.back().raster.reset();
}
}
void save(const std::string& fpath, const std::string& prjname = "");
void set_statistics(const std::vector<double> statistics);
};
} // namespace sla
} // namespace Slic3r
#endif // SLARASTERWRITER_HPP

View file

@ -44,7 +44,7 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj,
// call the status callback in each iteration but the actual value may be
// the same for subsequent iterations (status goes from 0 to 100 but
// iterations can be many more)
auto objfunc = [&emesh, &status, &statuscb, max_tries]
auto objfunc = [&emesh, &status, &statuscb, &stopcond, max_tries]
(double rx, double ry, double rz)
{
EigenMesh3D& m = emesh;
@ -91,7 +91,7 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj,
}
// report status
statuscb( unsigned(++status * 100.0/max_tries) );
if(!stopcond()) statuscb( unsigned(++status * 100.0/max_tries) );
return score;
};

View file

@ -747,8 +747,8 @@ void SLAPrint::process()
{
// We apply the printer correction offset here.
if(clpr_offs != 0)
po.m_model_slices[id] =
offset_ex(po.m_model_slices[id], float(clpr_offs));
po.m_model_slices[id] =
offset_ex(po.m_model_slices[id], float(clpr_offs));
mit->set_model_slice_idx(po, id); ++mit;
}
@ -1014,7 +1014,7 @@ void SLAPrint::process()
namespace sl = libnest2d::shapelike; // For algorithms
// If the raster has vertical orientation, we will flip the coordinates
bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
// bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
// Set up custom union and diff functions for clipper polygons
auto polyunion = [] (const ClipperPolygons& subjects)
@ -1072,9 +1072,9 @@ void SLAPrint::process()
// get polygons for all instances in the object
auto get_all_polygons =
[flpXY](const ExPolygons& input_polygons,
const std::vector<SLAPrintObject::Instance>& instances,
bool is_lefthanded)
[](const ExPolygons& input_polygons,
const std::vector<SLAPrintObject::Instance>& instances,
bool is_lefthanded)
{
ClipperPolygons polygons;
polygons.reserve(input_polygons.size() * instances.size());
@ -1088,7 +1088,7 @@ void SLAPrint::process()
// We need to reverse if flpXY OR is_lefthanded is true but
// not if both are true which is a logical inequality (XOR)
bool needreverse = flpXY != is_lefthanded;
bool needreverse = /*flpXY !=*/ is_lefthanded;
// should be a move
poly.Contour.reserve(polygon.contour.size() + 1);
@ -1123,10 +1123,10 @@ void SLAPrint::process()
sl::translate(poly, ClipperPoint{instances[i].shift(X),
instances[i].shift(Y)});
if (flpXY) {
for(auto& p : poly.Contour) std::swap(p.X, p.Y);
for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
}
// if (flpXY) {
// for(auto& p : poly.Contour) std::swap(p.X, p.Y);
// for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
// }
polygons.emplace_back(std::move(poly));
}
@ -1295,35 +1295,11 @@ void SLAPrint::process()
auto rasterize = [this]() {
if(canceled()) return;
// collect all the keys
// If the raster has vertical orientation, we will flip the coordinates
bool flpXY = m_printer_config.display_orientation.getInt() ==
SLADisplayOrientation::sladoPortrait;
{ // create a raster printer for the current print parameters
// I don't know any better
auto& ocfg = m_objects.front()->m_config;
auto& matcfg = m_material_config;
auto& printcfg = m_printer_config;
double w = printcfg.display_width.getFloat();
double h = printcfg.display_height.getFloat();
auto pw = unsigned(printcfg.display_pixels_x.getInt());
auto ph = unsigned(printcfg.display_pixels_y.getInt());
double lh = ocfg.layer_height.getFloat();
double exp_t = matcfg.exposure_time.getFloat();
double iexp_t = matcfg.initial_exposure_time.getFloat();
double gamma = m_printer_config.gamma_correction.getFloat();
if(flpXY) { std::swap(w, h); std::swap(pw, ph); }
m_printer.reset(
new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t,
flpXY? SLAPrinter::RO_PORTRAIT :
SLAPrinter::RO_LANDSCAPE,
gamma));
double layerh = m_default_object_config.layer_height.getFloat();
m_printer.reset(new SLAPrinter(m_printer_config,
m_material_config,
layerh));
}
// Allocate space for all the layers
@ -1511,6 +1487,8 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
"display_height",
"display_pixels_x",
"display_pixels_y",
"display_mirror_x",
"display_mirror_y",
"display_orientation"
};

View file

@ -3,11 +3,11 @@
#include <mutex>
#include "PrintBase.hpp"
#include "PrintExport.hpp"
//#include "PrintExport.hpp"
#include "SLA/SLARasterWriter.hpp"
#include "Point.hpp"
#include "MTUtils.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
#include "Zipper.hpp"
namespace Slic3r {
@ -326,37 +326,6 @@ struct SLAPrintStatistics
}
};
// The implementation of creating zipped archives with wxWidgets
template<> class LayerWriter<Zipper> {
Zipper m_zip;
public:
LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {}
void next_entry(const std::string& fname) { m_zip.add_entry(fname); }
void binary_entry(const std::string& fname,
const std::uint8_t* buf,
size_t l)
{
m_zip.add_entry(fname, buf, l);
}
template<class T> inline LayerWriter& operator<<(T&& arg) {
m_zip << std::forward<T>(arg); return *this;
}
bool is_ok() const {
return true; // m_zip blows up if something goes wrong...
}
// After finalize, no writing to the archive will have an effect. The only
// valid operation is to dispose the object calling the destructor which
// should close the file. This method can throw and signal potential errors
// when flushing the archive. This is why its present.
void finalize() { m_zip.finalize(); }
};
/**
* @brief This class is the high level FSM for the SLA printing process.
*
@ -389,11 +358,10 @@ public:
// Returns true if the last step was finished with success.
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
template<class Fmt = Zipper>
inline void export_raster(const std::string& fpath,
const std::string& projectname = "")
const std::string& projectname = "")
{
if(m_printer) m_printer->save<Fmt>(fpath, projectname);
if(m_printer) m_printer->save(fpath, projectname);
}
const PrintObjects& objects() const { return m_objects; }
@ -454,7 +422,7 @@ public:
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
private:
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
using SLAPrinter = sla::SLARasterWriter;
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
// Implement same logic as in SLAPrintObject