Add the full source of BambuStudio

using version 1.0.10
This commit is contained in:
lane.wei 2022-07-15 23:37:19 +08:00 committed by Lane.Wei
parent 30bcadab3e
commit 1555904bef
3771 changed files with 1251328 additions and 0 deletions

3142
src/libslic3r/Format/3mf.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
#ifndef slic3r_Format_3mf_hpp_
#define slic3r_Format_3mf_hpp_
namespace Slic3r {
/* The format for saving the SLA points was changing in the past. This enum holds the latest version that is being currently used.
* Examples of the Slic3r_PE_sla_support_points.txt for historically used versions:
* version 0 : object_id=1|-12.055421 -2.658771 10.000000
object_id=2|-14.051745 -3.570338 5.000000
// no header and x,y,z positions of the points)
* version 1 : ThreeMF_support_points_version=1
object_id=1|-12.055421 -2.658771 10.000000 0.4 0.0
object_id=2|-14.051745 -3.570338 5.000000 0.6 1.0
// introduced header with version number; x,y,z,head_size,is_new_island)
*/
enum {
support_points_format_version = 1
};
enum {
drain_holes_format_version = 1
};
class Model;
struct ConfigSubstitutionContext;
class DynamicPrintConfig;
struct ThumbnailData;
// Load the content of a 3mf file into the given model and preset bundle.
extern bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version);
// Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr, bool zip64 = true);
} // namespace Slic3r
#endif /* slic3r_Format_3mf_hpp_ */

1394
src/libslic3r/Format/AMF.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
#ifndef slic3r_Format_AMF_hpp_
#define slic3r_Format_AMF_hpp_
namespace Slic3r {
class Model;
class DynamicPrintConfig;
// Load the content of an amf file into the given model and configuration.
extern bool load_amf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, bool* use_inches);
//BBS: remove amf export
// Save the given model and the config data into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
//extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
} // namespace Slic3r
#endif /* slic3r_Format_AMF_hpp_ */

View file

@ -0,0 +1,140 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../TriangleMesh.hpp"
#include "OBJ.hpp"
#include "objparser.hpp"
#include <string>
#include <boost/log/trivial.hpp>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#else
#define DIR_SEPARATOR '/'
#endif
namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr)
{
if (meshptr == nullptr)
return false;
// Parse the OBJ file.
ObjParser::ObjData data;
if (! ObjParser::objparse(path, data)) {
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path;
return false;
}
// Count the faces and verify, that all faces are triangular.
size_t num_faces = 0;
size_t num_quads = 0;
for (size_t i = 0; i < data.vertices.size(); ++ i) {
// Find the end of face.
size_t j = i;
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
if (num_face_vertices > 4) {
// Non-triangular and non-quad faces are not supported as of now.
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with more than 4 vertices.";
return false;
} else if (num_face_vertices < 3) {
// Non-triangular and non-quad faces are not supported as of now.
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with less than 2 vertices.";
return false;
}
if (num_face_vertices == 4)
++ num_quads;
++ num_faces;
i = j;
}
}
// Convert ObjData into indexed triangle set.
indexed_triangle_set its;
size_t num_vertices = data.coordinates.size() / 4;
its.vertices.reserve(num_vertices);
its.indices.reserve(num_faces + num_quads);
for (size_t i = 0; i < num_vertices; ++ i) {
size_t j = i << 2;
its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
}
int indices[4];
for (size_t i = 0; i < data.vertices.size();)
if (data.vertices[i].coordIdx == -1)
++ i;
else {
int cnt = 0;
while (i < data.vertices.size())
if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
break;
} else {
assert(cnt < 4);
if (vertex.coordIdx < 0 || vertex.coordIdx >= int(its.vertices.size())) {
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
return false;
}
indices[cnt ++] = vertex.coordIdx;
}
if (cnt) {
assert(cnt == 3 || cnt == 4);
// Insert one or two faces (triangulate a quad).
its.indices.emplace_back(indices[0], indices[1], indices[2]);
if (cnt == 4)
its.indices.emplace_back(indices[0], indices[2], indices[3]);
}
}
*meshptr = TriangleMesh(std::move(its));
if (meshptr->empty()) {
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
return false;
}
if (meshptr->volume() < 0)
meshptr->flip_triangles();
return true;
}
bool load_obj(const char *path, Model *model, const char *object_name_in)
{
TriangleMesh mesh;
bool ret = load_obj(path, &mesh);
if (ret) {
std::string object_name;
if (object_name_in == nullptr) {
const char *last_slash = strrchr(path, DIR_SEPARATOR);
object_name.assign((last_slash == nullptr) ? path : last_slash + 1);
} else
object_name.assign(object_name_in);
model->add_object(object_name.c_str(), path, std::move(mesh));
}
return ret;
}
bool store_obj(const char *path, TriangleMesh *mesh)
{
//FIXME returning false even if write failed.
mesh->WriteOBJFile(path);
return true;
}
bool store_obj(const char *path, ModelObject *model_object)
{
TriangleMesh mesh = model_object->mesh();
return store_obj(path, &mesh);
}
bool store_obj(const char *path, Model *model)
{
TriangleMesh mesh = model->mesh();
return store_obj(path, &mesh);
}
}; // namespace Slic3r

View file

@ -0,0 +1,20 @@
#ifndef slic3r_Format_OBJ_hpp_
#define slic3r_Format_OBJ_hpp_
namespace Slic3r {
class TriangleMesh;
class Model;
class ModelObject;
// Load an OBJ file into a provided model.
extern bool load_obj(const char *path, TriangleMesh *mesh);
extern bool load_obj(const char *path, Model *model, const char *object_name = nullptr);
extern bool store_obj(const char *path, TriangleMesh *mesh);
extern bool store_obj(const char *path, ModelObject *model);
extern bool store_obj(const char *path, Model *model);
}; // namespace Slic3r
#endif /* slic3r_Format_OBJ_hpp_ */

View file

@ -0,0 +1,518 @@
#include "SL1.hpp"
#include "GCode/ThumbnailData.hpp"
#include "libslic3r/Time.hpp"
#include <boost/log/trivial.hpp>
#include <boost/filesystem.hpp>
#include "libslic3r/Zipper.hpp"
#include "libslic3r/SLAPrint.hpp"
#include <sstream>
#include "libslic3r/Exception.hpp"
#include "libslic3r/SlicesToTriangleMesh.hpp"
#include "libslic3r/MarchingSquares.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/MTUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SLA/RasterBase.hpp"
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/PNGReadWrite.hpp"
#include "libslic3r/LocalesUtils.hpp"
#include <boost/property_tree/ini_parser.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp>
namespace marchsq {
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
using Rst = Slic3r::png::ImageGreyscale;
// The type of pixel cell in the raster
using ValueType = uint8_t;
// Value at a given position
static uint8_t get(const Rst &rst, size_t row, size_t col)
{
return rst.get(row, col);
}
// Number of rows and cols of the raster
static size_t rows(const Rst &rst) { return rst.rows; }
static size_t cols(const Rst &rst) { return rst.cols; }
};
} // namespace marchsq
namespace Slic3r {
namespace {
struct PNGBuffer { std::vector<uint8_t> buf; std::string fname; };
struct ArchiveData {
boost::property_tree::ptree profile, config;
std::vector<PNGBuffer> images;
};
static const constexpr char *CONFIG_FNAME = "config.ini";
static const constexpr char *PROFILE_FNAME = "prusaslicer.ini";
boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry,
MZ_Archive & zip)
{
std::string buf(size_t(entry.m_uncomp_size), '\0');
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
buf.data(), buf.size(), 0))
throw Slic3r::FileIOError(zip.get_errorstr());
boost::property_tree::ptree tree;
std::stringstream ss(buf);
boost::property_tree::read_ini(ss, tree);
return tree;
}
PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
MZ_Archive & zip,
const std::string & name)
{
std::vector<uint8_t> buf(entry.m_uncomp_size);
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
buf.data(), buf.size(), 0))
throw Slic3r::FileIOError(zip.get_errorstr());
return {std::move(buf), (name.empty() ? entry.m_filename : name)};
}
ArchiveData extract_sla_archive(const std::string &zipfname,
const std::string &exclude)
{
ArchiveData arch;
// Little RAII
struct Arch: public MZ_Archive {
Arch(const std::string &fname) {
if (!open_zip_reader(&arch, fname))
throw Slic3r::FileIOError(get_errorstr());
}
~Arch() { close_zip_reader(&arch); }
} zip (zipfname);
mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch);
for (mz_uint i = 0; i < num_entries; ++i)
{
mz_zip_archive_file_stat entry;
if (mz_zip_reader_file_stat(&zip.arch, i, &entry))
{
std::string name = entry.m_filename;
boost::algorithm::to_lower(name);
if (boost::algorithm::contains(name, exclude)) continue;
if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip);
if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip);
if (boost::filesystem::path(name).extension().string() == ".png") {
auto it = std::lower_bound(
arch.images.begin(), arch.images.end(), PNGBuffer{{}, name},
[](const PNGBuffer &r1, const PNGBuffer &r2) {
return std::less<std::string>()(r1.fname, r2.fname);
});
arch.images.insert(it, read_png(entry, zip, name));
}
}
}
return arch;
}
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
double px_w, double px_h)
{
auto polys = reserve_vector<ExPolygon>(rings.size());
for (const marchsq::Ring &ring : rings) {
Polygon poly; Points &pts = poly.points;
pts.reserve(ring.size());
for (const marchsq::Coord &crd : ring)
pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h));
polys.emplace_back(poly);
}
// TODO: Is a union necessary?
return union_ex(polys);
}
template<class Fn> void foreach_vertex(ExPolygon &poly, Fn &&fn)
{
for (auto &p : poly.contour.points) fn(p);
for (auto &h : poly.holes)
for (auto &p : h.points) fn(p);
}
void invert_raster_trafo(ExPolygons & expolys,
const sla::RasterBase::Trafo &trafo,
coord_t width,
coord_t height)
{
if (trafo.flipXY) std::swap(height, width);
for (auto &expoly : expolys) {
if (trafo.mirror_y)
foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); });
if (trafo.mirror_x)
foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); });
expoly.translate(-trafo.center_x, -trafo.center_y);
if (trafo.flipXY)
foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); });
if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) {
expoly.contour.reverse();
for (auto &h : expoly.holes) h.reverse();
}
}
}
struct RasterParams {
sla::RasterBase::Trafo trafo; // Raster transformations
coord_t width, height; // scaled raster dimensions (not resolution)
double px_h, px_w; // pixel dimesions
marchsq::Coord win; // marching squares window size
};
RasterParams get_raster_params(const DynamicPrintConfig &cfg)
{
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
auto *opt_disp_rows = cfg.option<ConfigOptionInt>("display_pixels_y");
auto *opt_disp_w = cfg.option<ConfigOptionFloat>("display_width");
auto *opt_disp_h = cfg.option<ConfigOptionFloat>("display_height");
auto *opt_mirror_x = cfg.option<ConfigOptionBool>("display_mirror_x");
auto *opt_mirror_y = cfg.option<ConfigOptionBool>("display_mirror_y");
auto *opt_orient = cfg.option<ConfigOptionEnum<SLADisplayOrientation>>("display_orientation");
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
throw MissingProfileError("Invalid SL1 / SL1S file");
RasterParams rstp;
rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1);
rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1);
rstp.trafo = sla::RasterBase::Trafo{opt_orient->value == sladoLandscape ?
sla::RasterBase::roLandscape :
sla::RasterBase::roPortrait,
{opt_mirror_x->value, opt_mirror_y->value}};
rstp.height = scaled(opt_disp_h->value);
rstp.width = scaled(opt_disp_w->value);
return rstp;
}
struct SliceParams { double layerh = 0., initial_layerh = 0.; };
SliceParams get_slice_params(const DynamicPrintConfig &cfg)
{
auto *opt_layerh = cfg.option<ConfigOptionFloat>("layer_height");
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
if (!opt_layerh || !opt_init_layerh)
throw MissingProfileError("Invalid SL1 / SL1S file");
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
}
std::vector<ExPolygons> extract_slices_from_sla_archive(
ArchiveData & arch,
const RasterParams & rstp,
std::function<bool(int)> progr)
{
auto jobdir = arch.config.get<std::string>("jobDir");
for (auto &c : jobdir) c = std::tolower(c);
std::vector<ExPolygons> slices(arch.images.size());
struct Status
{
double incr, val, prev;
bool stop = false;
tbb::spin_mutex mutex = {};
} st {100. / slices.size(), 0., 0.};
tbb::parallel_for(size_t(0), arch.images.size(),
[&arch, &slices, &st, &rstp, progr](size_t i) {
// Status indication guarded with the spinlock
{
std::lock_guard<tbb::spin_mutex> lck(st.mutex);
if (st.stop) return;
st.val += st.incr;
double curr = std::round(st.val);
if (curr > st.prev) {
st.prev = curr;
st.stop = !progr(int(curr));
}
}
png::ImageGreyscale img;
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
if (!png::decode_png(rb, img)) return;
uint8_t isoval = 128;
auto rings = marchsq::execute(img, isoval, rstp.win);
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
// Invert the raster transformations indicated in the profile metadata
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
slices[i] = std::move(expolys);
});
if (st.stop) slices = {};
return slices;
}
} // namespace
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
{
ArchiveData arch = extract_sla_archive(zipfname, "png");
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
}
// If the profile is missing from the archive (older PS versions did not have
// it), profile_out's initial value will be used as fallback. profile_out will be empty on
// function return if the archive did not contain any profile.
ConfigSubstitutions import_sla_archive(
const std::string & zipfname,
Vec2i windowsize,
indexed_triangle_set & out,
DynamicPrintConfig & profile_out,
std::function<bool(int)> progr)
{
// Ensure minimum window size for marching squares
windowsize.x() = std::max(2, windowsize.x());
windowsize.y() = std::max(2, windowsize.y());
std::string exclude_entries{"thumbnail"};
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
DynamicPrintConfig profile_in, profile_use;
ConfigSubstitutions config_substitutions =
profile_in.load(arch.profile,
ForwardCompatibilitySubstitutionRule::Enable);
if (profile_in.empty()) { // missing profile... do guess work
// try to recover the layer height from the config.ini which was
// present in all versions of sl1 files.
if (auto lh_opt = arch.config.find("layerHeight");
lh_opt != arch.config.not_found())
{
auto lh_str = lh_opt->second.data();
size_t pos;
double lh = string_to_double_decimal_point(lh_str, &pos);
if (pos) { // TODO: verify that pos is 0 when parsing fails
profile_out.set("layer_height", lh);
profile_out.set("initial_layer_height", lh);
}
}
}
// If the archive contains an empty profile, use the one that was passed as output argument
// then replace it with the readed profile to report that it was empty.
profile_use = profile_in.empty() ? profile_out : profile_in;
profile_out = profile_in;
RasterParams rstp = get_raster_params(profile_use);
rstp.win = {windowsize.y(), windowsize.x()};
SliceParams slicp = get_slice_params(profile_use);
std::vector<ExPolygons> slices =
extract_slices_from_sla_archive(arch, rstp, progr);
if (!slices.empty())
out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
return config_substitutions;
}
using ConfMap = std::map<std::string, std::string>;
namespace {
std::string to_ini(const ConfMap &m)
{
std::string ret;
for (auto &param : m) ret += param.first + " = " + param.second + "\n";
return ret;
}
std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key)
{
std::string ret;
if (cfg.has(key)) {
auto opt = cfg.option(key);
if (opt) ret = opt->serialize();
}
return ret;
}
void fill_iniconf(ConfMap &m, const SLAPrint &print)
{
CNumericLocalesSetter locales_setter; // for to_string
auto &cfg = print.full_print_config();
m["layerHeight"] = get_cfg_value(cfg, "layer_height");
m["expTime"] = get_cfg_value(cfg, "exposure_time");
m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time");
m["expUserProfile"] = get_cfg_value(cfg, "material_print_speed") == "slow" ? "1" : "0";
m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id");
m["printerModel"] = get_cfg_value(cfg, "printer_model");
m["printerVariant"] = get_cfg_value(cfg, "printer_variant");
m["printerProfile"] = get_cfg_value(cfg, "printer_settings_id");
m["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id");
m["fileCreationTimestamp"] = Utils::utc_timestamp();
m["prusaSlicerVersion"] = SLIC3R_BUILD_ID;
SLAPrintStatistics stats = print.print_statistics();
// Set statistics values to the printer
double used_material = (stats.objects_used_material +
stats.support_used_material) / 1000;
int num_fade = print.default_object_config().faded_layers.getInt();
num_fade = num_fade >= 0 ? num_fade : 0;
m["usedMaterial"] = std::to_string(used_material);
m["numFade"] = std::to_string(num_fade);
m["numSlow"] = std::to_string(stats.slow_layers_count);
m["numFast"] = std::to_string(stats.fast_layers_count);
m["printTime"] = std::to_string(stats.estimated_print_time);
bool hollow_en = false;
auto it = print.objects().begin();
while (!hollow_en && it != print.objects().end())
hollow_en = (*it++)->config().hollowing_enable;
m["hollow"] = hollow_en ? "1" : "0";
m["action"] = "print";
}
void fill_slicerconf(ConfMap &m, const SLAPrint &print)
{
using namespace std::literals::string_view_literals;
// Sorted list of config keys, which shall not be stored into the ini.
static constexpr auto banned_keys = {
"compatible_printers"sv,
"compatible_prints"sv
};
assert(std::is_sorted(banned_keys.begin(), banned_keys.end()));
auto is_banned = [](const std::string &key) {
return std::binary_search(banned_keys.begin(), banned_keys.end(), key);
};
auto &cfg = print.full_print_config();
for (const std::string &key : cfg.keys())
if (! is_banned(key) && ! cfg.option(key)->is_nil())
m[key] = cfg.opt_serialize(key);
}
} // namespace
std::unique_ptr<sla::RasterBase> SL1Archive::create_raster() const
{
sla::RasterBase::Resolution res;
sla::RasterBase::PixelDim pxdim;
std::array<bool, 2> mirror;
double w = m_cfg.display_width.getFloat();
double h = m_cfg.display_height.getFloat();
auto pw = size_t(m_cfg.display_pixels_x.getInt());
auto ph = size_t(m_cfg.display_pixels_y.getInt());
mirror[X] = m_cfg.display_mirror_x.getBool();
mirror[Y] = m_cfg.display_mirror_y.getBool();
auto ro = m_cfg.display_orientation.getInt();
sla::RasterBase::Orientation orientation =
ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait :
sla::RasterBase::roLandscape;
if (orientation == sla::RasterBase::roPortrait) {
std::swap(w, h);
std::swap(pw, ph);
}
res = sla::RasterBase::Resolution{pw, ph};
pxdim = sla::RasterBase::PixelDim{w / pw, h / ph};
sla::RasterBase::Trafo tr{orientation, mirror};
double gamma = m_cfg.gamma_correction.getFloat();
return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr);
}
sla::RasterEncoder SL1Archive::get_encoder() const
{
return sla::PNGRasterEncoder{};
}
void SL1Archive::export_print(Zipper& zipper,
const SLAPrint &print,
const std::string &prjname)
{
std::string project =
prjname.empty() ?
boost::filesystem::path(zipper.get_filename()).stem().string() :
prjname;
ConfMap iniconf, slicerconf;
fill_iniconf(iniconf, print);
iniconf["jobDir"] = project;
fill_slicerconf(slicerconf, print);
try {
zipper.add_entry("config.ini");
zipper << to_ini(iniconf);
zipper.add_entry("prusaslicer.ini");
zipper << to_ini(slicerconf);
size_t i = 0;
for (const sla::EncodedRaster &rst : m_layers) {
std::string imgname = project + string_printf("%.5d", i++) + "." +
rst.extension();
zipper.add_entry(imgname.c_str(), rst.data(), rst.size());
}
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
// Rethrow the exception
throw;
}
}
} // namespace Slic3r

View file

@ -0,0 +1,64 @@
#ifndef ARCHIVETRAITS_HPP
#define ARCHIVETRAITS_HPP
#include <string>
#include "libslic3r/Zipper.hpp"
#include "libslic3r/SLAPrint.hpp"
namespace Slic3r {
class SL1Archive: public SLAArchive {
SLAPrinterConfig m_cfg;
protected:
std::unique_ptr<sla::RasterBase> create_raster() const override;
sla::RasterEncoder get_encoder() const override;
public:
SL1Archive() = default;
explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "");
void export_print(const std::string &fname, const SLAPrint &print, const std::string &projectname = "")
{
Zipper zipper(fname);
export_print(zipper, print, projectname);
}
void apply(const SLAPrinterConfig &cfg) override
{
auto diff = m_cfg.diff(cfg);
if (!diff.empty()) {
m_cfg.apply_only(cfg, diff);
m_layers = {};
}
}
};
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
ConfigSubstitutions import_sla_archive(
const std::string & zipfname,
Vec2i windowsize,
indexed_triangle_set & out,
DynamicPrintConfig & profile,
std::function<bool(int)> progr = [](int) { return true; });
inline ConfigSubstitutions import_sla_archive(
const std::string & zipfname,
Vec2i windowsize,
indexed_triangle_set & out,
std::function<bool(int)> progr = [](int) { return true; })
{
DynamicPrintConfig profile;
return import_sla_archive(zipfname, windowsize, out, profile, progr);
}
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
} // namespace Slic3r::sla
#endif // ARCHIVETRAITS_HPP

View file

@ -0,0 +1,378 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../TriangleMesh.hpp"
#include "STEP.hpp"
#include <string>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#else
#define DIR_SEPARATOR '/'
#endif
#include "STEPCAFControl_Reader.hxx"
#include "BRepMesh_IncrementalMesh.hxx"
#include "Interface_Static.hxx"
#include "XCAFDoc_DocumentTool.hxx"
#include "XCAFDoc_ShapeTool.hxx"
#include "XCAFApp_Application.hxx"
#include "TopoDS_Solid.hxx"
#include "TopoDS_Compound.hxx"
#include "TopoDS_Builder.hxx"
#include "TopoDS.hxx"
#include "TDataStd_Name.hxx"
#include "BRepBuilderAPI_Transform.hxx"
#include "TopExp_Explorer.hxx"
#include "TopExp_Explorer.hxx"
#include "BRep_Tool.hxx"
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
const int LOAD_STEP_STAGE_READ_FILE = 0;
const int LOAD_STEP_STAGE_GET_SOLID = 1;
const int LOAD_STEP_STAGE_GET_MESH = 2;
namespace Slic3r {
bool StepPreProcessor::preprocess(const char* path, std::string &output_path)
{
boost::nowide::ifstream infile(path);
if (!infile.good()) {
throw Slic3r::RuntimeError(std::string("Load step file failed.\nCannot open file for reading.\n"));
return false;
}
boost::filesystem::path temp_path(temporary_dir());
std::string temp_step_path = temp_path.string() + "/temp.step";
boost::nowide::remove(temp_step_path.c_str());
boost::nowide::ofstream temp_file(temp_step_path, std::ios::app);
std::string temp_line;
while (std::getline(infile, temp_line)) {
if (m_encode_type == EncodedType::UTF8) {
//BBS: continue to judge whether is other type
if (isUtf8(temp_line)) {
//BBS: do nothing, but must be checked before checking whether is GBK
}
//BBS: not utf8, then maybe GBK
else if (isGBK(temp_line)) {
m_encode_type = EncodedType::GBK;
}
//BBS: not UTF8 and not GBK, then maybe some kind of special encoded type which we can't handle
// Load the step as UTF and user will see garbage characters in slicer but we have no solution at the moment
else {
m_encode_type = EncodedType::OTHER;
}
}
if (m_encode_type == EncodedType::GBK)
//BBS: transform to UTF8 format if is GBK
//todo: use gbkToUtf8 function to replace
temp_file << decode_path(temp_line.c_str()) << std::endl;
else
temp_file << temp_line.c_str() << std::endl;
}
temp_file.close();
infile.close();
if (m_encode_type == EncodedType::GBK) {
output_path = temp_step_path;
} else {
boost::nowide::remove(temp_step_path.c_str());
output_path = std::string(path);
}
return true;
}
bool StepPreProcessor::isUtf8File(const char* path)
{
boost::nowide::ifstream infile(path);
if (!infile.good()) {
throw Slic3r::RuntimeError(std::string("Load step file failed.\nCannot open file for reading.\n"));
return false;
}
std::string temp_line;
while (std::getline(infile, temp_line)) {
if (!isUtf8(temp_line)) {
infile.close();
return false;
}
}
infile.close();
return true;
}
bool StepPreProcessor::isUtf8(const std::string str)
{
size_t num = 0;
int i = 0;
while (i < str.length()) {
if ((str[i] & 0x80) == 0x00) {
i++;
} else if ((num = preNum(str[i])) > 2) {
i++;
for (int j = 0; j < num - 1; j++) {
if ((str[i] & 0xc0) != 0x80)
return false;
i++;
}
} else {
return false;
}
}
return true;
}
bool StepPreProcessor::isGBK(const std::string str) {
size_t i = 0;
while (i < str.length()) {
if (str[i] <= 0x7f) {
i++;
continue;
} else {
if (str[i] >= 0x81 &&
str[i] <= 0xfe &&
str[i + 1] >= 0x40 &&
str[i + 1] <= 0xfe &&
str[i + 1] != 0xf7) {
i += 2;
continue;
}
else {
return false;
}
}
}
return true;
}
int StepPreProcessor::preNum(const unsigned char byte) {
unsigned char mask = 0x80;
int num = 0;
for (int i = 0; i < 8; i++) {
if ((byte & mask) == mask) {
mask = mask >> 1;
num++;
} else {
break;
}
}
return num;
}
struct NamedSolid {
NamedSolid(const TopoDS_Shape& s,
const std::string& n) : solid{s}, name{n} {}
const TopoDS_Shape solid;
const std::string name;
};
static void getNamedSolids(const TopLoc_Location& location, const std::string& prefix,
unsigned int& id, const Handle(XCAFDoc_ShapeTool) shapeTool,
const TDF_Label label, std::vector<NamedSolid>& namedSolids) {
TDF_Label referredLabel{label};
if (shapeTool->IsReference(label))
shapeTool->GetReferredShape(label, referredLabel);
std::string name;
Handle(TDataStd_Name) shapeName;
if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName))
name = TCollection_AsciiString(shapeName->Get()).ToCString();
if (name == "")
name = std::to_string(id++);
std::string fullName{name};
TopLoc_Location localLocation = location * shapeTool->GetLocation(label);
TDF_LabelSequence components;
if (shapeTool->GetComponents(referredLabel, components)) {
for (Standard_Integer compIndex = 1; compIndex <= components.Length(); ++compIndex) {
getNamedSolids(localLocation, fullName, id, shapeTool, components.Value(compIndex), namedSolids);
}
} else {
TopoDS_Shape shape;
shapeTool->GetShape(referredLabel, shape);
TopAbs_ShapeEnum shape_type = shape.ShapeType();
BRepBuilderAPI_Transform transform(shape, localLocation, Standard_True);
switch (shape_type) {
case TopAbs_COMPOUND:
namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), fullName);
break;
case TopAbs_COMPSOLID:
namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), fullName);
break;
case TopAbs_SOLID:
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName);
break;
default:
break;
}
}
}
bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepIsUtf8Fn isUtf8Fn)
{
bool cb_cancel = false;
if (proFn) {
proFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
if (cb_cancel)
return false;
}
if (!StepPreProcessor::isUtf8File(path) && isUtf8Fn)
isUtf8Fn(false);
std::string file_after_preprocess = std::string(path);
std::vector<NamedSolid> namedSolids;
Handle(TDocStd_Document) document;
Handle(XCAFApp_Application) application = XCAFApp_Application::GetApplication();
application->NewDocument(file_after_preprocess.c_str(), document);
STEPCAFControl_Reader reader;
reader.SetNameMode(true);
//BBS: Todo, read file is slow which cause the progress_bar no update and gui no response
IFSelect_ReturnStatus stat = reader.ReadFile(file_after_preprocess.c_str());
if (stat != IFSelect_RetDone || !reader.Transfer(document)) {
application->Close(document);
throw std::logic_error{ std::string{"Could not read '"} + path + "'" };
return false;
}
Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(document->Main());
TDF_LabelSequence topLevelShapes;
shapeTool->GetFreeShapes(topLevelShapes);
unsigned int id{1};
Standard_Integer topShapeLength = topLevelShapes.Length() + 1;
for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) {
if (proFn) {
proFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
if (cb_cancel) {
shapeTool.reset(nullptr);
application->Close(document);
return false;
}
}
getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids);
}
ModelObject* new_object = model->add_object();
const char *last_slash = strrchr(path, DIR_SEPARATOR);
new_object->name.assign((last_slash == nullptr) ? path : last_slash + 1);
new_object->input_file = path;
for (size_t i = 0; i < namedSolids.size(); ++i) {
if (proFn) {
proFn(LOAD_STEP_STAGE_GET_MESH, i, namedSolids.size(), cb_cancel);
if (cb_cancel) {
model->delete_object(new_object);
shapeTool.reset(nullptr);
application->Close(document);
return false;
}
}
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
//BBS: calculate total number of the nodes and triangles
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
if (aNbTriangles == 0) {
//BBS: No triangulation on the shape.
continue;
}
stl_file stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)aNbTriangles;
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
//BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
//BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape& aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
//BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
//BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
Standard_Integer anId[3];
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED) {
//BBS: swap 1, 2.
Standard_Integer aTmpIdx = anId[1];
anId[1] = anId[2];
anId[2] = aTmpIdx;
}
//BBS: Update nodes according to the offset.
anId[0] += aNodeOffset;
anId[1] += aNodeOffset;
anId[2] += aNodeOffset;
//BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] - 1].cast<float>();
facet.vertex[1] = points[anId[1] - 1].cast<float>();
facet.vertex[2] = points[anId[2] - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
TriangleMesh triangle_mesh;
triangle_mesh.from_stl(stl);
ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int)model->objects.size() - 1;
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
}
shapeTool.reset(nullptr);
application->Close(document);
//BBS: no valid shape from the step, delete the new object as well
if (new_object->volumes.size() == 0) {
model->delete_object(new_object);
return false;
}
return true;
}
}; // namespace Slic3r

View file

@ -0,0 +1,42 @@
#ifndef slic3r_Format_STEP_hpp_
#define slic3r_Format_STEP_hpp_
namespace Slic3r {
class TriangleMesh;
class ModelObject;
typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;
//BBS: Load an step file into a provided model.
extern bool load_step(const char *path, Model *model, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
//BBS: Used to detect what kind of encoded type is used in name field of step
// If is encoded in UTF8, the file don't need to be handled, then return the original path directly.
// If is encoded in GBK, then translate to UTF8 and generate a new temporary step file.
// If is encoded in Other type, we can't handled, then treat as UTF8. In this case, the name is garbage
// characters.
// By preprocessing, at least we can avoid garbage characters if the name field is encoded by GBK.
class StepPreProcessor {
enum class EncodedType : unsigned char
{
UTF8,
GBK,
OTHER
};
public:
bool preprocess(const char* path, std::string &output_path);
static bool isUtf8File(const char* path);
private:
static bool isUtf8(const std::string str);
static bool isGBK(const std::string str);
static int preNum(const unsigned char byte);
//BBS: default is UTF8 for most step file.
EncodedType m_encode_type = EncodedType::UTF8;
};
}; // namespace Slic3r
#endif /* slic3r_Format_STEP_hpp_ */

View file

@ -0,0 +1,62 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../TriangleMesh.hpp"
#include "STL.hpp"
#include <string>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#else
#define DIR_SEPARATOR '/'
#endif
namespace Slic3r {
bool load_stl(const char *path, Model *model, const char *object_name_in)
{
TriangleMesh mesh;
if (! mesh.ReadSTLFile(path)) {
// die "Failed to open $file\n" if !-e $path;
return false;
}
if (mesh.empty()) {
// die "This STL file couldn't be read because it's empty.\n"
return false;
}
std::string object_name;
if (object_name_in == nullptr) {
const char *last_slash = strrchr(path, DIR_SEPARATOR);
object_name.assign((last_slash == nullptr) ? path : last_slash + 1);
} else
object_name.assign(object_name_in);
model->add_object(object_name.c_str(), path, std::move(mesh));
return true;
}
bool store_stl(const char *path, TriangleMesh *mesh, bool binary)
{
if (binary)
mesh->write_binary(path);
else
mesh->write_ascii(path);
//FIXME returning false even if write failed.
return true;
}
bool store_stl(const char *path, ModelObject *model_object, bool binary)
{
TriangleMesh mesh = model_object->mesh();
return store_stl(path, &mesh, binary);
}
bool store_stl(const char *path, Model *model, bool binary)
{
TriangleMesh mesh = model->mesh();
return store_stl(path, &mesh, binary);
}
}; // namespace Slic3r

View file

@ -0,0 +1,18 @@
#ifndef slic3r_Format_STL_hpp_
#define slic3r_Format_STL_hpp_
namespace Slic3r {
class TriangleMesh;
class ModelObject;
// Load an STL file into a provided model.
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr);
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
extern bool store_stl(const char *path, ModelObject *model_object, bool binary);
extern bool store_stl(const char *path, Model *model, bool binary);
}; // namespace Slic3r
#endif /* slic3r_Format_STL_hpp_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
#ifndef BBS_3MF_hpp_
#define BBS_3MF_hpp_
#include "../GCode/ThumbnailData.hpp"
#include "libslic3r/ProjectTask.hpp"
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include <functional>
namespace Slic3r {
class Model;
struct ConfigSubstitutionContext;
class DynamicPrintConfig;
class Preset;
struct FilamentInfo;
struct ThumbnailData;
#define GCODE_FILE_FORMAT "Metadata/plate_%1%.gcode"
#define THUMBNAIL_FILE_FORMAT "Metadata/plate_%1%.png"
#define PATTERN_FILE_FORMAT "Metadata/plate_%1%_pattern_layer_0.png"
#define PATTERN_CONFIG_FILE_FORMAT "Metadata/plate_%1%.json"
#define EMBEDDED_PRINT_FILE_FORMAT "Metadata/process_settings_%1%.config"
#define EMBEDDED_FILAMENT_FILE_FORMAT "Metadata/filament_settings_%1%.config"
#define EMBEDDED_PRINTER_FILE_FORMAT "Metadata/machine_settings_%1%.config"
//BBS: define assistant struct to store temporary variable during exporting 3mf
class PackingTemporaryData
{
public:
std::string _3mf_thumbnail;
std::string _3mf_printer_thumbnail_middle;
std::string _3mf_printer_thumbnail_small;
PackingTemporaryData() {}
};
//BBS: define plate data list related structures
struct PlateData
{
PlateData(int plate_id, std::set<std::pair<int, int>> &obj_to_inst_list, bool lock_state) : plate_index(plate_id), locked(lock_state)
{
objects_and_instances.clear();
for (std::set<std::pair<int, int>>::iterator it = obj_to_inst_list.begin(); it != obj_to_inst_list.end(); ++it)
objects_and_instances.emplace_back(it->first, it->second);
}
PlateData() : plate_index(-1), locked(false)
{
objects_and_instances.clear();
}
~PlateData()
{
objects_and_instances.clear();
}
void parse_filament_info(GCodeProcessorResult *result);
int plate_index;
std::vector<std::pair<int, int>> objects_and_instances;
std::string gcode_file;
std::string gcode_file_md5;
std::string thumbnail_file;
ThumbnailData plate_thumbnail;
ThumbnailData pattern_thumbnail;
std::string pattern_file;
std::string gcode_prediction;
std::string gcode_weight;
std::vector<FilamentInfo> slice_flaments_info;
bool is_sliced_valid = false;
bool toolpath_outside {false};
std::string get_gcode_prediction_str() {
return gcode_prediction;
}
std::string get_gcode_weight_str() {
return gcode_weight;
}
bool locked;
};
// BBS: encrypt
enum class SaveStrategy
{
Default = 0,
FullPathSources = 1,
Zip64 = 1 << 1,
ProductionExt = 1 << 2,
SecureContentExt = 1 << 3,
WithGcode = 1 << 4,
Silence = 1 << 5,
SkipStatic = 1 << 6,
SkipModel = 1 << 7,
WithSliceInfo = 1 << 8,
SplitModel = 0x1000 | ProductionExt,
Encrypted = SecureContentExt | SplitModel,
Backup = 0x10000 | WithGcode | Silence | SkipStatic | SplitModel,
};
inline SaveStrategy operator | (SaveStrategy lhs, SaveStrategy rhs)
{
using T = std::underlying_type_t <SaveStrategy>;
return static_cast<SaveStrategy>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
inline bool operator & (SaveStrategy & lhs, SaveStrategy rhs)
{
using T = std::underlying_type_t <SaveStrategy>;
return ((static_cast<T>(lhs) & static_cast<T>(rhs))) == static_cast<T>(rhs);
}
enum class LoadStrategy
{
Default = 0,
AddDefaultInstances = 1,
CheckVersion = 2,
LoadModel = 4,
LoadConfig = 8,
LoadAuxiliary = 16,
Silence = 32,
ImperialUnits = 64,
Restore = 0x10000 | LoadModel | LoadConfig | LoadAuxiliary | Silence,
};
inline LoadStrategy operator | (LoadStrategy lhs, LoadStrategy rhs)
{
using T = std::underlying_type_t <LoadStrategy>;
return static_cast<LoadStrategy>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
inline bool operator & (LoadStrategy & lhs, LoadStrategy rhs)
{
using T = std::underlying_type_t <LoadStrategy>;
return (static_cast<T>(lhs) & static_cast<T>(rhs)) == static_cast<T>(rhs);
}
const int EXPORT_STAGE_OPEN_3MF = 0;
const int EXPORT_STAGE_CONTENT_TYPES = 1;
const int EXPORT_STAGE_ADD_THUMBNAILS = 2;
const int EXPORT_STAGE_ADD_RELATIONS = 3;
const int EXPORT_STAGE_ADD_MODELS = 4;
const int EXPORT_STAGE_ADD_LAYER_RANGE = 5;
const int EXPORT_STAGE_ADD_SUPPORT = 6;
const int EXPORT_STAGE_ADD_CUSTOM_GCODE = 7;
const int EXPORT_STAGE_ADD_PRINT_CONFIG = 8;
const int EXPORT_STAGE_ADD_PROJECT_CONFIG = 9;
const int EXPORT_STAGE_ADD_CONFIG_FILE = 10;
const int EXPORT_STAGE_ADD_SLICE_INFO = 11;
const int EXPORT_STAGE_ADD_GCODE = 12;
const int EXPORT_STAGE_ADD_AUXILIARIES = 13;
const int EXPORT_STAGE_FINISH = 14;
const int IMPORT_STAGE_RESTORE = 0;
const int IMPORT_STAGE_OPEN = 1;
const int IMPORT_STAGE_READ_FILES = 2;
const int IMPORT_STAGE_EXTRACT = 3;
const int IMPORT_STAGE_LOADING_OBJECTS = 4;
const int IMPORT_STAGE_LOADING_PLATES = 5;
const int IMPORT_STAGE_FINISH = 6;
const int IMPORT_STAGE_ADD_INSTANCE = 7;
const int IMPORT_STAGE_UPDATE_GCODE = 8;
const int IMPORT_STAGE_CHECK_MODE_GCODE = 9;
const int UPDATE_GCODE_RESULT = 10;
const int IMPORT_LOAD_CONFIG = 11;
const int IMPORT_LOAD_MODEL_OBJECTS = 12;
//BBS export 3mf progress
typedef std::function<void(int export_stage, int current, int total, bool& cancel)> Export3mfProgressFn;
typedef std::function<void(int import_stage, int current, int total, bool& cancel)> Import3mfProgressFn;
typedef std::vector<PlateData*> PlateDataPtrs;
typedef std::map<int, PlateData*> PlateDataMaps;
struct StoreParams
{
const char* path;
Model* model = nullptr;
PlateDataPtrs plate_data_list;
int export_plate_idx = -1;
std::vector<Preset*> project_presets;
DynamicPrintConfig* config;
std::vector<ThumbnailData*> thumbnail_data;
std::vector<ThumbnailData*> calibration_thumbnail_data;
SaveStrategy strategy = SaveStrategy::Zip64;
Export3mfProgressFn proFn = nullptr;
std::vector<PlateBBoxData*> id_bboxes;
BBLProject* project = nullptr;
BBLProfile* profile = nullptr;
StoreParams() {}
};
//BBS: add plate data list related logic
// add restore logic
// Load the content of a 3mf file into the given model and preset bundle.
extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector<Preset*>* project_presets, bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn = nullptr, LoadStrategy strategy = LoadStrategy::Default, BBLProject *project = nullptr);
extern std::string bbs_3mf_get_thumbnail(const char * path);
//BBS: add plate data list related logic
// add backup logic
// Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
/*
extern bool store_bbs_3mf(const char* path,
Model* model,
PlateDataPtrs& plate_data_list,
std::vector<Preset*>& project_presets,
const DynamicPrintConfig* config,
bool fullpath_sources,
const std::vector<ThumbnailData*>& thumbnail_data,
bool zip64 = true,
bool skip_static = false,
Export3mfProgressFn proFn = nullptr,
bool silence = true);
*/
extern bool store_bbs_3mf(StoreParams& store_params);
extern void release_PlateData_list(PlateDataPtrs& plate_data_list);
// backup & restore project
extern void save_object_mesh(ModelObject& object);
extern void delete_object_mesh(ModelObject& object);
extern void backup_soon();
extern void remove_backup(Model& model, bool removeAll);
extern void set_backup_interval(long interval);
extern void set_backup_callback(std::function<void(int)> callback);
extern void run_backup_ui_tasks();
extern bool has_restore_data(std::string & path, std::string & origin);
extern void put_other_changes();
extern void clear_other_changes(bool backup);
extern bool has_other_changes(bool backup);
class SaveObjectGaurd {
public:
SaveObjectGaurd(ModelObject& object);
~SaveObjectGaurd();
};
} // namespace Slic3r
#endif /* BBS_3MF_hpp_ */

View file

@ -0,0 +1,588 @@
#include <stdlib.h>
#include <string.h>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp>
#include "objparser.hpp"
#include "libslic3r/LocalesUtils.hpp"
namespace ObjParser {
static bool obj_parseline(const char *line, ObjData &data)
{
#define EATWS() while (*line == ' ' || *line == '\t') ++ line
if (*line == 0)
return true;
assert(Slic3r::is_decimal_separator_point());
// Ignore whitespaces at the beginning of the line.
//FIXME is this a good idea?
EATWS();
char c1 = *line ++;
switch (c1) {
case '#':
// Comment, ignore the rest of the line.
break;
case 'v':
{
// Parse vertex geometry (position, normal, texture coordinates)
char c2 = *line ++;
switch (c2) {
case 't':
{
// vt - vertex texture parameter
// u v [w], w == 0 (or w == 1)
char c2 = *line ++;
if (c2 != ' ' && c2 != '\t')
return false;
EATWS();
char *endptr = 0;
double u = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double v = 0;
if (*line != 0) {
v = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
}
double w = 0;
if (*line != 0) {
w = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
}
if (*line != 0)
return false;
data.textureCoordinates.push_back((float)u);
data.textureCoordinates.push_back((float)v);
data.textureCoordinates.push_back((float)w);
break;
}
case 'n':
{
// vn - vertex normal
// x y z
char c2 = *line ++;
if (c2 != ' ' && c2 != '\t')
return false;
EATWS();
char *endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
if (*line != 0)
return false;
data.normals.push_back((float)x);
data.normals.push_back((float)y);
data.normals.push_back((float)z);
break;
}
case 'p':
{
// vp - vertex parameter
char c2 = *line ++;
if (c2 != ' ' && c2 != '\t')
return false;
EATWS();
char *endptr = 0;
double u = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
double v = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
double w = 0;
if (*line != 0) {
w = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
}
if (*line != 0)
return false;
data.parameters.push_back((float)u);
data.parameters.push_back((float)v);
data.parameters.push_back((float)w);
break;
}
default:
{
// v - vertex geometry
if (c2 != ' ' && c2 != '\t')
return false;
EATWS();
char *endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
double w = 1.0;
if (*line != 0) {
w = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
}
// the following check is commented out because there may be obj files containing extra data, as those generated by Meshlab,
// see https://dev.prusa3d.com/browse/SPE-1019 for an example,
// and this would lead to a crash because no vertex would be stored
// if (*line != 0)
// return false;
data.coordinates.push_back((float)x);
data.coordinates.push_back((float)y);
data.coordinates.push_back((float)z);
data.coordinates.push_back((float)w);
break;
}
}
break;
}
case 'f':
{
// face
EATWS();
if (*line == 0)
return false;
// current vertex to be parsed
ObjVertex vertex;
char *endptr = 0;
while (*line != 0) {
// Parse a single vertex reference.
vertex.coordIdx = 0;
vertex.normalIdx = 0;
vertex.textureCoordIdx = 0;
vertex.coordIdx = strtol(line, &endptr, 10);
// Coordinate has to be defined
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
return false;
line = endptr;
if (*line == '/') {
++ line;
// Texture coordinate index may be missing after a 1st slash, but then the normal index has to be present.
if (*line != '/') {
// Parse the texture coordinate index.
vertex.textureCoordIdx = strtol(line, &endptr, 10);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
return false;
line = endptr;
}
if (*line == '/') {
// Parse normal index.
++ line;
vertex.normalIdx = strtol(line, &endptr, 10);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
}
}
if (vertex.coordIdx < 0)
vertex.coordIdx += (int)data.coordinates.size() / 4;
else
-- vertex.coordIdx;
if (vertex.normalIdx < 0)
vertex.normalIdx += (int)data.normals.size() / 3;
else
-- vertex.normalIdx;
if (vertex.textureCoordIdx < 0)
vertex.textureCoordIdx += (int)data.textureCoordinates.size() / 3;
else
-- vertex.textureCoordIdx;
data.vertices.push_back(vertex);
EATWS();
}
vertex.coordIdx = -1;
vertex.normalIdx = -1;
vertex.textureCoordIdx = -1;
data.vertices.push_back(vertex);
break;
}
case 'm':
{
if (*(line ++) != 't' ||
*(line ++) != 'l' ||
*(line ++) != 'l' ||
*(line ++) != 'i' ||
*(line ++) != 'b')
return false;
// mtllib [external .mtl file name]
// printf("mtllib %s\r\n", line);
EATWS();
data.mtllibs.push_back(std::string(line));
break;
}
case 'u':
{
if (*(line ++) != 's' ||
*(line ++) != 'e' ||
*(line ++) != 'm' ||
*(line ++) != 't' ||
*(line ++) != 'l')
return false;
// usemtl [material name]
// printf("usemtl %s\r\n", line);
EATWS();
ObjUseMtl usemtl;
usemtl.vertexIdxFirst = (int)data.vertices.size();
usemtl.name = line;
data.usemtls.push_back(usemtl);
break;
}
case 'o':
{
// o [object name]
EATWS();
while (*line != ' ' && *line != '\t' && *line != 0)
++ line;
// copy name to line.
EATWS();
if (*line != 0)
return false;
ObjObject object;
object.vertexIdxFirst = (int)data.vertices.size();
object.name = line;
data.objects.push_back(object);
break;
}
case 'g':
{
// g [group name]
// printf("group %s\r\n", line);
ObjGroup group;
group.vertexIdxFirst = (int)data.vertices.size();
group.name = line;
data.groups.push_back(group);
break;
}
case 's':
{
// s 1 / off
char c2 = *line ++;
if (c2 != ' ' && c2 != '\t')
return false;
EATWS();
char *endptr = 0;
long g = strtol(line, &endptr, 10);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
if (*line != 0)
return false;
ObjSmoothingGroup group;
group.vertexIdxFirst = (int)data.vertices.size();
group.smoothingGroupID = g;
data.smoothingGroups.push_back(group);
break;
}
default:
BOOST_LOG_TRIVIAL(error) << "ObjParser: Unknown command: " << c1;
break;
}
return true;
}
bool objparse(const char *path, ObjData &data)
{
Slic3r::CNumericLocalesSetter locales_setter;
FILE *pFile = boost::nowide::fopen(path, "rt");
if (pFile == 0)
return false;
try {
char buf[65536 * 2];
size_t len = 0;
size_t lenPrev = 0;
while ((len = ::fread(buf + lenPrev, 1, 65536, pFile)) != 0) {
len += lenPrev;
size_t lastLine = 0;
for (size_t i = 0; i < len; ++ i)
if (buf[i] == '\r' || buf[i] == '\n') {
buf[i] = 0;
char *c = buf + lastLine;
while (*c == ' ' || *c == '\t')
++ c;
//FIXME check the return value and exit on error?
// Will it break parsing of some obj files?
obj_parseline(c, data);
lastLine = i + 1;
}
lenPrev = len - lastLine;
if (lenPrev > 65536) {
BOOST_LOG_TRIVIAL(error) << "ObjParser: Excessive line length";
::fclose(pFile);
return false;
}
memmove(buf, buf + lastLine, lenPrev);
}
}
catch (std::bad_alloc&) {
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
}
::fclose(pFile);
// printf("vertices: %d\r\n", data.vertices.size() / 4);
// printf("coords: %d\r\n", data.coordinates.size());
return true;
}
bool objparse(std::istream &stream, ObjData &data)
{
Slic3r::CNumericLocalesSetter locales_setter;
try {
char buf[65536 * 2];
size_t len = 0;
size_t lenPrev = 0;
while ((len = size_t(stream.read(buf + lenPrev, 65536).gcount())) != 0) {
len += lenPrev;
size_t lastLine = 0;
for (size_t i = 0; i < len; ++ i)
if (buf[i] == '\r' || buf[i] == '\n') {
buf[i] = 0;
char *c = buf + lastLine;
while (*c == ' ' || *c == '\t')
++ c;
obj_parseline(c, data);
lastLine = i + 1;
}
lenPrev = len - lastLine;
memmove(buf, buf + lastLine, lenPrev);
}
}
catch (std::bad_alloc&) {
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
return false;
}
return true;
}
template<typename T>
bool savevector(FILE *pFile, const std::vector<T> &v)
{
size_t cnt = v.size();
::fwrite(&cnt, 1, sizeof(cnt), pFile);
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
if (! v.empty())
::fwrite(&v.front(), 1, sizeof(T) * cnt, pFile);
return true;
}
bool savevector(FILE *pFile, const std::vector<std::string> &v)
{
size_t cnt = v.size();
::fwrite(&cnt, 1, sizeof(cnt), pFile);
for (size_t i = 0; i < cnt; ++ i) {
size_t len = v[i].size();
::fwrite(&len, 1, sizeof(cnt), pFile);
::fwrite(v[i].c_str(), 1, len, pFile);
}
return true;
}
template<typename T>
bool savevectornameidx(FILE *pFile, const std::vector<T> &v)
{
size_t cnt = v.size();
::fwrite(&cnt, 1, sizeof(cnt), pFile);
for (size_t i = 0; i < cnt; ++ i) {
::fwrite(&v[i].vertexIdxFirst, 1, sizeof(int), pFile);
size_t len = v[i].name.size();
::fwrite(&len, 1, sizeof(cnt), pFile);
::fwrite(v[i].name.c_str(), 1, len, pFile);
}
return true;
}
template<typename T>
bool loadvector(FILE *pFile, std::vector<T> &v)
{
v.clear();
size_t cnt = 0;
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
return false;
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
if (cnt != 0) {
v.assign(cnt, T());
if (::fread(&v.front(), sizeof(T), cnt, pFile) != cnt)
return false;
}
return true;
}
bool loadvector(FILE *pFile, std::vector<std::string> &v)
{
v.clear();
size_t cnt = 0;
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
return false;
v.reserve(cnt);
for (size_t i = 0; i < cnt; ++ i) {
size_t len = 0;
if (::fread(&len, sizeof(len), 1, pFile) != 1)
return false;
std::string s(" ", len);
if (::fread(s.data(), 1, len, pFile) != len)
return false;
v.push_back(std::move(s));
}
return true;
}
template<typename T>
bool loadvectornameidx(FILE *pFile, std::vector<T> &v)
{
v.clear();
size_t cnt = 0;
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
return false;
v.assign(cnt, T());
for (size_t i = 0; i < cnt; ++ i) {
if (::fread(&v[i].vertexIdxFirst, sizeof(int), 1, pFile) != 1)
return false;
size_t len = 0;
if (::fread(&len, sizeof(len), 1, pFile) != 1)
return false;
v[i].name.assign(" ", len);
if (::fread(v[i].name.data(), 1, len, pFile) != len)
return false;
}
return true;
}
bool objbinsave(const char *path, const ObjData &data)
{
FILE *pFile = boost::nowide::fopen(path, "wb");
if (pFile == 0)
return false;
size_t version = 1;
::fwrite(&version, 1, sizeof(version), pFile);
bool result =
savevector(pFile, data.coordinates) &&
savevector(pFile, data.textureCoordinates) &&
savevector(pFile, data.normals) &&
savevector(pFile, data.parameters) &&
savevector(pFile, data.mtllibs) &&
savevectornameidx(pFile, data.usemtls) &&
savevectornameidx(pFile, data.objects) &&
savevectornameidx(pFile, data.groups) &&
savevector(pFile, data.smoothingGroups) &&
savevector(pFile, data.vertices);
::fclose(pFile);
return result;
}
bool objbinload(const char *path, ObjData &data)
{
FILE *pFile = boost::nowide::fopen(path, "rb");
if (pFile == 0)
return false;
data.version = 0;
if (::fread(&data.version, sizeof(data.version), 1, pFile) != 1)
return false;
if (data.version != 1)
return false;
bool result =
loadvector(pFile, data.coordinates) &&
loadvector(pFile, data.textureCoordinates) &&
loadvector(pFile, data.normals) &&
loadvector(pFile, data.parameters) &&
loadvector(pFile, data.mtllibs) &&
loadvectornameidx(pFile, data.usemtls) &&
loadvectornameidx(pFile, data.objects) &&
loadvectornameidx(pFile, data.groups) &&
loadvector(pFile, data.smoothingGroups) &&
loadvector(pFile, data.vertices);
::fclose(pFile);
return result;
}
template<typename T>
bool vectorequal(const std::vector<T> &v1, const std::vector<T> &v2)
{
if (v1.size() != v2.size())
return false;
for (size_t i = 0; i < v1.size(); ++ i)
if (! (v1[i] == v2[i]))
return false;
return true;
}
bool vectorequal(const std::vector<std::string> &v1, const std::vector<std::string> &v2)
{
if (v1.size() != v2.size())
return false;
for (size_t i = 0; i < v1.size(); ++ i)
if (v1[i].compare(v2[i]) != 0)
return false;
return true;
}
extern bool objequal(const ObjData &data1, const ObjData &data2)
{
//FIXME ignore version number
// version;
return
vectorequal(data1.coordinates, data2.coordinates) &&
vectorequal(data1.textureCoordinates, data2.textureCoordinates) &&
vectorequal(data1.normals, data2.normals) &&
vectorequal(data1.parameters, data2.parameters) &&
vectorequal(data1.mtllibs, data2.mtllibs) &&
vectorequal(data1.usemtls, data2.usemtls) &&
vectorequal(data1.objects, data2.objects) &&
vectorequal(data1.groups, data2.groups) &&
vectorequal(data1.vertices, data2.vertices);
}
} // namespace ObjParser

View file

@ -0,0 +1,111 @@
#ifndef slic3r_Format_objparser_hpp_
#define slic3r_Format_objparser_hpp_
#include <string>
#include <vector>
#include <istream>
namespace ObjParser {
struct ObjVertex
{
int coordIdx;
int textureCoordIdx;
int normalIdx;
};
inline bool operator==(const ObjVertex &v1, const ObjVertex &v2)
{
return
v1.coordIdx == v2.coordIdx &&
v1.textureCoordIdx == v2.textureCoordIdx &&
v1.normalIdx == v2.normalIdx;
}
struct ObjUseMtl
{
int vertexIdxFirst;
std::string name;
};
inline bool operator==(const ObjUseMtl &v1, const ObjUseMtl &v2)
{
return
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.name.compare(v2.name) == 0;
}
struct ObjObject
{
int vertexIdxFirst;
std::string name;
};
inline bool operator==(const ObjObject &v1, const ObjObject &v2)
{
return
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.name.compare(v2.name) == 0;
}
struct ObjGroup
{
int vertexIdxFirst;
std::string name;
};
inline bool operator==(const ObjGroup &v1, const ObjGroup &v2)
{
return
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.name.compare(v2.name) == 0;
}
struct ObjSmoothingGroup
{
int vertexIdxFirst;
int smoothingGroupID;
};
inline bool operator==(const ObjSmoothingGroup &v1, const ObjSmoothingGroup &v2)
{
return
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.smoothingGroupID == v2.smoothingGroupID;
}
struct ObjData {
// Version of the data structure for load / store in the private binary format.
int version;
// x, y, z, w
std::vector<float> coordinates;
// u, v, w
std::vector<float> textureCoordinates;
// x, y, z
std::vector<float> normals;
// u, v, w
std::vector<float> parameters;
std::vector<std::string> mtllibs;
std::vector<ObjUseMtl> usemtls;
std::vector<ObjObject> objects;
std::vector<ObjGroup> groups;
std::vector<ObjSmoothingGroup> smoothingGroups;
// List of faces, delimited by an ObjVertex with all members set to -1.
std::vector<ObjVertex> vertices;
};
extern bool objparse(const char *path, ObjData &data);
extern bool objparse(std::istream &stream, ObjData &data);
extern bool objbinsave(const char *path, const ObjData &data);
extern bool objbinload(const char *path, ObjData &data);
extern bool objequal(const ObjData &data1, const ObjData &data2);
} // namespace ObjParser
#endif /* slic3r_Format_objparser_hpp_ */