mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-24 09:11:23 -06:00
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer
This commit is contained in:
commit
27459a9072
1145 changed files with 2435 additions and 1497 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
142
src/libslic3r/MinAreaBoundingBox.cpp
Normal file
142
src/libslic3r/MinAreaBoundingBox.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
59
src/libslic3r/MinAreaBoundingBox.hpp
Normal file
59
src/libslic3r/MinAreaBoundingBox.hpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
136
src/libslic3r/SLA/SLARasterWriter.cpp
Normal file
136
src/libslic3r/SLA/SLARasterWriter.cpp
Normal 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
|
||||
167
src/libslic3r/SLA/SLARasterWriter.hpp
Normal file
167
src/libslic3r/SLA/SLARasterWriter.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue