Model centered when exporting to png. Added some comments to the rasterization.
This commit is contained in:
parent
0f552832da
commit
6b5c0073a7
4 changed files with 122 additions and 81 deletions
xs/src/libslic3r
|
@ -15,10 +15,6 @@
|
||||||
// For png export of the sliced model
|
// For png export of the sliced model
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
//#include <wx/dcmemory.h>
|
|
||||||
//#include <wx/bitmap.h>
|
|
||||||
//#include <wx/image.h>
|
|
||||||
//#include <wx/graphics.h>
|
|
||||||
|
|
||||||
#include "Rasterizer/Rasterizer.hpp"
|
#include "Rasterizer/Rasterizer.hpp"
|
||||||
#include "tbb/parallel_for.h"
|
#include "tbb/parallel_for.h"
|
||||||
|
@ -1254,22 +1250,57 @@ void Print::set_status(int percent, const std::string &message)
|
||||||
printf("Print::status %d => %s\n", percent, message.c_str());
|
printf("Print::status %d => %s\n", percent, message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interface for a file printer of the slices. Implementation can be an SVG
|
||||||
|
* or PNG printer or any other format.
|
||||||
|
*
|
||||||
|
* The format argument spefies the output format of the printer and it enables
|
||||||
|
* different implementations of this class template for each supported format.
|
||||||
|
*
|
||||||
|
*/
|
||||||
template<Print::FilePrinterFormat format>
|
template<Print::FilePrinterFormat format>
|
||||||
class FilePrinter {
|
class FilePrinter {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
|
||||||
void drawPolygon(const ExPolygon& p, unsigned lyr);
|
void drawPolygon(const ExPolygon& p, unsigned lyr);
|
||||||
|
|
||||||
|
// Tell the printer how many layers should it consider.
|
||||||
void layers(unsigned layernum);
|
void layers(unsigned layernum);
|
||||||
|
|
||||||
|
// Get the number of layers in the print.
|
||||||
unsigned layers() const;
|
unsigned layers() const;
|
||||||
|
|
||||||
|
/* Switch the a particular layer. If there where less layers then the
|
||||||
|
* specified layer number than an appripriate number of layers will be
|
||||||
|
* allocated in the printer.
|
||||||
|
*/
|
||||||
void beginLayer(unsigned layer);
|
void beginLayer(unsigned layer);
|
||||||
|
|
||||||
|
// Allocate a new layer on top of the last and switch to it.
|
||||||
void beginLayer();
|
void beginLayer();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 conmprimation and so on.
|
||||||
|
*/
|
||||||
void finishLayer(unsigned layer);
|
void finishLayer(unsigned layer);
|
||||||
|
|
||||||
|
// Finish the top layer.
|
||||||
void finishLayer();
|
void finishLayer();
|
||||||
|
|
||||||
|
// Save all the layers into the file (or dir) specified in the path argument
|
||||||
void save(const std::string& path);
|
void save(const std::string& path);
|
||||||
|
|
||||||
|
// Save only the selected layer to the file specified in path argument.
|
||||||
void saveLayer(unsigned lyr, const std::string& path);
|
void saveLayer(unsigned lyr, const std::string& path);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<> // Implementation for PNG raster output
|
||||||
class FilePrinter<Print::FilePrinterFormat::PNG> {
|
class FilePrinter<Print::FilePrinterFormat::PNG> {
|
||||||
|
// 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<std::pair<Raster, std::stringstream>> layers_rst_;
|
std::vector<std::pair<Raster, std::stringstream>> layers_rst_;
|
||||||
Raster::Resolution res_;
|
Raster::Resolution res_;
|
||||||
Raster::PixelDim pxdim_;
|
Raster::PixelDim pxdim_;
|
||||||
|
@ -1341,70 +1372,24 @@ public:
|
||||||
std::fstream out(loc, std::fstream::out | std::fstream::binary);
|
std::fstream out(loc, std::fstream::out | std::fstream::binary);
|
||||||
if(out.good()) {
|
if(out.good()) {
|
||||||
layers_rst_[i].first.save(out, Raster::Compression::PNG);
|
layers_rst_[i].first.save(out, Raster::Compression::PNG);
|
||||||
}
|
} /*else {
|
||||||
|
some logging should be done here...
|
||||||
|
}*/
|
||||||
out.close();
|
out.close();
|
||||||
layers_rst_[i].first.reset();
|
layers_rst_[i].first.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//template<>
|
|
||||||
//class FilePrinter<Print::FilePrinterFormat::PNG> {
|
|
||||||
// wxBitmap bitmap_;
|
|
||||||
// std::unique_ptr<wxMemoryDC> dc_;
|
|
||||||
// std::unique_ptr<wxGraphicsContext> gc_;
|
|
||||||
// double pxw_;
|
|
||||||
// double pxh_;
|
|
||||||
//public:
|
|
||||||
// inline FilePrinter(unsigned width_px, unsigned height_px,
|
|
||||||
// double width_mm, double height_mm):
|
|
||||||
// bitmap_(width_px, height_px),
|
|
||||||
// dc_(new wxMemoryDC(bitmap_)),
|
|
||||||
// gc_(wxGraphicsContext::Create(*dc_)),
|
|
||||||
// pxw_(width_mm/width_px),
|
|
||||||
// pxh_(height_mm/width_px)
|
|
||||||
// {
|
|
||||||
// gc_->SetAntialiasMode(wxANTIALIAS_DEFAULT);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// FilePrinter(const FilePrinter& ) = delete;
|
|
||||||
// FilePrinter(FilePrinter&& m):
|
|
||||||
// bitmap_(std::move(m.bitmap_)), dc_(std::move(m.dc_)),
|
|
||||||
// gc_(std::move(m.gc_)), pxw_(m.pxw_), pxh_(m.pxh_) {}
|
|
||||||
|
|
||||||
// void drawPolygon(const Polygon& p) {
|
|
||||||
|
|
||||||
// gc_->SetPen(*wxWHITE_PEN);
|
|
||||||
// std::vector<wxPoint2DDouble> points;
|
|
||||||
// points.reserve(p.points.size());
|
|
||||||
|
|
||||||
// for(auto pp : p.points) {
|
|
||||||
// points.emplace_back(
|
|
||||||
// std::round(pp.x * SCALING_FACTOR/pxw_),
|
|
||||||
// std::round(pp.y * SCALING_FACTOR/pxh_)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// gc_->DrawLines(points.size(), points.data());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void finish() {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void save(const std::string& path) {
|
|
||||||
// if(!bitmap_.SaveFile(path, wxBITMAP_TYPE_PNG)) {
|
|
||||||
// std::cout << "fail for " << path << std::endl;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//};
|
|
||||||
|
|
||||||
template<Print::FilePrinterFormat format, class...Args>
|
template<Print::FilePrinterFormat format, class...Args>
|
||||||
void Print::print_to(std::string dirpath, Args...args)
|
void Print::print_to(std::string dirpath,
|
||||||
|
double width_mm,
|
||||||
|
double height_mm,
|
||||||
|
Args...args)
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string dir = dirpath;
|
std::string dir = dirpath;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32 // Making dirpath end with a directory separator on all platforms
|
||||||
if(dir.back() != '\\') dir.push_back('\\');
|
if(dir.back() != '\\') dir.push_back('\\');
|
||||||
#else
|
#else
|
||||||
if(dir.back() != '/') dir.push_back('/');
|
if(dir.back() != '/') dir.push_back('/');
|
||||||
|
@ -1412,34 +1397,50 @@ void Print::print_to(std::string dirpath, Args...args)
|
||||||
|
|
||||||
LayerPtrs layers;
|
LayerPtrs layers;
|
||||||
|
|
||||||
|
// Merge the sliced layers wit hthe support layers
|
||||||
std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o){
|
std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o){
|
||||||
layers.insert(layers.end(), o->layers.begin(), o->layers.end());
|
layers.insert(layers.end(), o->layers.begin(), o->layers.end());
|
||||||
layers.insert(layers.end(), o->support_layers.begin(),
|
layers.insert(layers.end(), o->support_layers.begin(),
|
||||||
o->support_layers.end());
|
o->support_layers.end());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sort layers by z coord
|
||||||
std::sort(layers.begin(), layers.end(), [](Layer *l1, Layer *l2){
|
std::sort(layers.begin(), layers.end(), [](Layer *l1, Layer *l2){
|
||||||
return l1->print_z < l2->print_z;
|
return l1->print_z < l2->print_z;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto print_bb = bounding_box();
|
auto print_bb = bounding_box();
|
||||||
|
|
||||||
FilePrinter<format> printer(std::forward<Args>(args)...);
|
// If the print does not fit into the print area we should cry about it.
|
||||||
printer.layers(layers.size());
|
assert(unscale(print_bb.size().x) <= width_mm ||
|
||||||
|
unscale(print_bb.size().y) <= height_mm);
|
||||||
|
|
||||||
auto process_layer = [&layers, &printer, print_bb, dir] (unsigned layer_id) {
|
// Offset for centering the print onto the print area
|
||||||
|
auto cx = scale_(width_mm)/2 - (print_bb.center().x - print_bb.min.x);
|
||||||
|
auto cy = scale_(height_mm)/2 - (print_bb.center().y - print_bb.min.y);
|
||||||
|
|
||||||
|
// Create the actual printer, forward any additional arguments to it.
|
||||||
|
FilePrinter<format> printer(std::forward<Args>(args)...);
|
||||||
|
printer.layers(layers.size()); // Allocate space for all the layers
|
||||||
|
|
||||||
|
// Method that prints one layer
|
||||||
|
auto process_layer = [this, &layers, &printer, print_bb, dir, cx, cy]
|
||||||
|
(unsigned layer_id)
|
||||||
|
{
|
||||||
Layer& l = *(layers[layer_id]);
|
Layer& l = *(layers[layer_id]);
|
||||||
|
|
||||||
ExPolygonCollection slices = l.slices;
|
ExPolygonCollection slices = l.slices; // Copy the layer slices
|
||||||
using Sl = ExPolygons::value_type;
|
|
||||||
|
// Sort the polygons in the layer
|
||||||
std::sort(slices.expolygons.begin(),
|
std::sort(slices.expolygons.begin(),
|
||||||
slices.expolygons.end(),
|
slices.expolygons.end(),
|
||||||
[](Sl a, Sl b){
|
[](const ExPolygon& a, const ExPolygon& b){
|
||||||
return a.contains(b.contour.first_point()) ? false : true;
|
return a.contains(b.contour.first_point()) ? false : true;
|
||||||
});
|
});
|
||||||
|
|
||||||
printer.beginLayer(layer_id);
|
printer.beginLayer(layer_id); // Switch to the appropriate layer
|
||||||
|
|
||||||
|
// Draw all the polygons in the slice to the actual layer.
|
||||||
std::for_each(l.object()->_shifted_copies.begin(),
|
std::for_each(l.object()->_shifted_copies.begin(),
|
||||||
l.object()->_shifted_copies.end(),
|
l.object()->_shifted_copies.end(),
|
||||||
[&] (Point d)
|
[&] (Point d)
|
||||||
|
@ -1449,25 +1450,34 @@ void Print::print_to(std::string dirpath, Args...args)
|
||||||
[&] (ExPolygon slice)
|
[&] (ExPolygon slice)
|
||||||
{
|
{
|
||||||
slice.translate(d.x, d.y);
|
slice.translate(d.x, d.y);
|
||||||
slice.translate(-print_bb.min.x, -print_bb.min.y);
|
slice.translate(-print_bb.min.x + cx, -print_bb.min.y + cy);
|
||||||
|
|
||||||
printer.drawPolygon(slice, layer_id);
|
printer.drawPolygon(slice, layer_id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
printer.finishLayer(layer_id);
|
if(has_support_material() && layer_id > 0) {
|
||||||
// printer.saveLayer(layer_id, dir);
|
std::cout << "support layer " << layer_id << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.finishLayer(layer_id); // Finish the layer for later saving it.
|
||||||
|
// printer.saveLayer(layer_id, dir); We could save the layer immediately
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tbb::parallel_for<size_t, decltype(process_layer)>(0, layers.size(), process_layer);
|
// Print all the layers in parallel
|
||||||
|
tbb::parallel_for<size_t, decltype(process_layer)>(0,
|
||||||
|
layers.size(),
|
||||||
|
process_layer);
|
||||||
|
|
||||||
|
// Save the print into the file system.
|
||||||
printer.save(dir);
|
printer.save(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Print::print_to_png(std::string dirpath, long width_px, long height_px,
|
void Print::print_to_png(std::string dirpath, long width_px, long height_px,
|
||||||
double width_mm, double height_mm) {
|
double width_mm, double height_mm) {
|
||||||
print_to<FilePrinterFormat::PNG>(dirpath, width_px, height_px,
|
print_to<FilePrinterFormat::PNG>(dirpath, width_mm, height_mm,
|
||||||
|
width_px, height_px,
|
||||||
width_mm, height_mm);
|
width_mm, height_mm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -316,14 +316,17 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<FilePrinterFormat format, class...Args>
|
template<FilePrinterFormat format, class...Args>
|
||||||
void print_to(std::string dirpath, Args...args);
|
void print_to(std::string dirpath,
|
||||||
|
double width_mm,
|
||||||
|
double height_mm,
|
||||||
|
Args...args);
|
||||||
|
|
||||||
void print_to_png(std::string dirpath, long width_px, long height_px,
|
void print_to_png(std::string dirpath, long width_px, long height_px,
|
||||||
double width_mm, double height_mm);
|
double width_mm, double height_mm);
|
||||||
|
|
||||||
void print_to_png(std::string dirpath) {
|
void print_to_png(std::string dirpath) {
|
||||||
// Where should this be specified?
|
// Will need some GUI dialogue perhaps for these to be specified.
|
||||||
print_to_png(dirpath, 1440, 2560, 40.0, 72.0);
|
print_to_png(dirpath, 1440, 2560, 68.0, 120.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "Rasterizer.hpp"
|
#include "Rasterizer.hpp"
|
||||||
|
#include <ExPolygon.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
// For rasterizing
|
// For rasterizing
|
||||||
|
@ -14,7 +16,7 @@
|
||||||
#include <agg/agg_rasterizer_scanline_aa.h>
|
#include <agg/agg_rasterizer_scanline_aa.h>
|
||||||
#include <agg/agg_path_storage.h>
|
#include <agg/agg_path_storage.h>
|
||||||
|
|
||||||
// For compression
|
// For png compression
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
inline char *strerror_r(int errnum, char *buf, size_t buflen) {
|
inline char *strerror_r(int errnum, char *buf, size_t buflen) {
|
||||||
strerror_s(buf, buflen, errnum);
|
strerror_s(buf, buflen, errnum);
|
||||||
|
@ -70,9 +72,12 @@ public:
|
||||||
agg::rasterizer_scanline_aa<> ras;
|
agg::rasterizer_scanline_aa<> ras;
|
||||||
agg::scanline_p8 scanlines;
|
agg::scanline_p8 scanlines;
|
||||||
|
|
||||||
ras.add_path(to_path(poly.contour));
|
auto&& path = to_path(poly.contour);
|
||||||
|
ras.add_path(path);
|
||||||
|
|
||||||
for(auto h : poly.holes) {
|
for(auto h : poly.holes) {
|
||||||
ras.add_path(to_path(h));
|
auto&& holepath = to_path(h);
|
||||||
|
ras.add_path(holepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
agg::render_scanlines(ras, scanlines, renderer_);
|
agg::render_scanlines(ras, scanlines, renderer_);
|
||||||
|
|
|
@ -2,20 +2,32 @@
|
||||||
#define RASTERIZER_HPP
|
#define RASTERIZER_HPP
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <ExPolygon.hpp>
|
#include <memory>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class ExPolygon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Raster captures an antialiased monochrome canvas where vectorial
|
||||||
|
* polygons can be rasterized. Fill color is always white and the background is
|
||||||
|
* black. Countours are antialiased.
|
||||||
|
*
|
||||||
|
* It also supports saving the raster data into a standard output stream in raw
|
||||||
|
* or PNG format.
|
||||||
|
*/
|
||||||
class Raster {
|
class Raster {
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> impl_;
|
std::unique_ptr<Impl> impl_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// Supported compression types
|
||||||
enum class Compression {
|
enum class Compression {
|
||||||
RAW,
|
RAW, //!> Uncompressed pixel data
|
||||||
PNG
|
PNG //!> PNG compression
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Type that represents a resolution in pixels.
|
||||||
struct Resolution {
|
struct Resolution {
|
||||||
unsigned width_px;
|
unsigned width_px;
|
||||||
unsigned height_px;
|
unsigned height_px;
|
||||||
|
@ -25,6 +37,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Types that represents the dimension of a pixel in millimeters.
|
||||||
struct PixelDim {
|
struct PixelDim {
|
||||||
double w_mm;
|
double w_mm;
|
||||||
double h_mm;
|
double h_mm;
|
||||||
|
@ -32,22 +45,32 @@ public:
|
||||||
w_mm(px_width_mm), h_mm(px_height_mm) {}
|
w_mm(px_width_mm), h_mm(px_height_mm) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Constructor taking the resolution and the pixel dimension.
|
||||||
explicit Raster(const Resolution& r, const PixelDim& pd );
|
explicit Raster(const Resolution& r, const PixelDim& pd );
|
||||||
Raster();
|
Raster();
|
||||||
~Raster();
|
|
||||||
Raster(const Raster& cpy);
|
Raster(const Raster& cpy);
|
||||||
Raster(Raster&& m);
|
Raster(Raster&& m);
|
||||||
|
~Raster();
|
||||||
|
|
||||||
|
/// Reallocated everything for the given resolution and pixel dimension.
|
||||||
void reset(const Resolution& r, const PixelDim& pd);
|
void reset(const Resolution& r, const PixelDim& pd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the allocated resources. Drawing in this state ends in
|
||||||
|
* unspecified behaviour.
|
||||||
|
*/
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
/// Get the resolution of the raster.
|
||||||
Resolution resolution() const;
|
Resolution resolution() const;
|
||||||
|
|
||||||
|
/// Clear the raster with black color.
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
/// Draw a polygon with holes.
|
||||||
void draw(const ExPolygon& poly);
|
void draw(const ExPolygon& poly);
|
||||||
|
|
||||||
|
/// Save the raster on the specified stream.
|
||||||
void save(std::ostream& stream, Compression comp = Compression::RAW);
|
void save(std::ostream& stream, Compression comp = Compression::RAW);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue