mirror of
https://github.com/QIDITECH/QIDIStudio.git
synced 2026-02-07 04:11:50 +03:00
update libslic3r
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
@@ -33,6 +34,11 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
||||
message = _L("load_obj: failed to parse");
|
||||
return false;
|
||||
}
|
||||
|
||||
obj_info.ml_region = data.ml_region;
|
||||
obj_info.ml_name = data.ml_name;
|
||||
obj_info.ml_id = data.ml_id;
|
||||
|
||||
bool exist_mtl = false;
|
||||
if (data.mtllibs.size() > 0) { // read mtl
|
||||
for (auto mtl_name : data.mtllibs) {
|
||||
@@ -40,16 +46,20 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
||||
continue;
|
||||
}
|
||||
exist_mtl = true;
|
||||
bool mtl_name_is_path = false;
|
||||
boost::filesystem::path mtl_abs_path(mtl_name);
|
||||
bool mtl_name_is_path = false;
|
||||
std::wstring wide_mtl_name = boost::locale::conv::to_utf<wchar_t>(mtl_name, "UTF-8");
|
||||
if (boost::istarts_with(wide_mtl_name,"./")){
|
||||
boost::replace_first(wide_mtl_name, "./", "");
|
||||
}
|
||||
boost::filesystem::path mtl_abs_path(wide_mtl_name);
|
||||
if (boost::filesystem::exists(mtl_abs_path)) {
|
||||
mtl_name_is_path = true;
|
||||
}
|
||||
boost::filesystem::path mtl_path;
|
||||
if (!mtl_name_is_path) {
|
||||
boost::filesystem::path full_path(path);
|
||||
std::string dir = full_path.parent_path().string();
|
||||
auto mtl_file = dir + "/" + mtl_name;
|
||||
auto dir = full_path.parent_path().wstring();
|
||||
auto mtl_file = dir + L"/" + wide_mtl_name;
|
||||
boost::filesystem::path temp_mtl_path(mtl_file);
|
||||
mtl_path = temp_mtl_path;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
#ifndef slic3r_Format_OBJ_hpp_
|
||||
#define slic3r_Format_OBJ_hpp_
|
||||
#include "libslic3r/Color.hpp"
|
||||
#include "objparser.hpp"
|
||||
#include <unordered_map>
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Model;
|
||||
class ModelObject;
|
||||
typedef std::function<void(std::vector<RGBA> &input_colors, bool is_single_color, std::vector<unsigned char> &filament_ids, unsigned char &first_extruder_id)> ObjImportColorFn;
|
||||
typedef std::function<void(std::vector<RGBA> &input_colors, bool is_single_color, std::vector<unsigned char> &filament_ids, unsigned char &first_extruder_id,
|
||||
std::string& ml_region, std::string& ml_name, std::string& ml_id)> ObjImportColorFn;
|
||||
// Load an OBJ file into a provided model.
|
||||
struct ObjInfo {
|
||||
std::vector<RGBA> vertex_colors;
|
||||
@@ -19,6 +21,9 @@ struct ObjInfo {
|
||||
std::unordered_map<int, std::string> uv_map_pngs;
|
||||
bool has_uv_png{false};
|
||||
|
||||
std::string ml_region;
|
||||
std::string ml_name;
|
||||
std::string ml_id;
|
||||
};
|
||||
extern bool load_obj(const char *path, TriangleMesh *mesh, ObjInfo &vertex_colors, std::string &message);
|
||||
extern bool load_obj(const char *path, Model *model, ObjInfo &vertex_colors, std::string &message, const char *object_name = nullptr);
|
||||
|
||||
@@ -33,9 +33,8 @@
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
|
||||
const double STEP_TRANS_CHORD_ERROR = 0.003;
|
||||
const double STEP_TRANS_ANGLE_RES = 0.5;
|
||||
#include "BRepTools.hxx"
|
||||
#include <IMeshTools_Parameters.hxx>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -166,13 +165,6 @@ int StepPreProcessor::preNum(const unsigned char byte) {
|
||||
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) {
|
||||
@@ -185,7 +177,7 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName))
|
||||
name = TCollection_AsciiString(shapeName->Get()).ToCString();
|
||||
|
||||
if (name == "")
|
||||
if (name == "" || !StepPreProcessor::isUtf8(name))
|
||||
name = std::to_string(id++);
|
||||
std::string fullName{name};
|
||||
|
||||
@@ -197,15 +189,19 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
}
|
||||
} else {
|
||||
TopoDS_Shape shape;
|
||||
TopExp_Explorer explorer;
|
||||
shapeTool->GetShape(referredLabel, shape);
|
||||
TopAbs_ShapeEnum shape_type = shape.ShapeType();
|
||||
BRepBuilderAPI_Transform transform(shape, localLocation, Standard_True);
|
||||
int i = 0;
|
||||
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);
|
||||
for (explorer.Init(transform.Shape(), TopAbs_SOLID); explorer.More(); explorer.Next()) {
|
||||
i++;
|
||||
const TopoDS_Shape& currentShape = explorer.Current();
|
||||
namedSolids.emplace_back(TopoDS::Solid(currentShape), fullName + "-SOLID-" + std::to_string(i));
|
||||
}
|
||||
break;
|
||||
case TopAbs_SOLID:
|
||||
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName);
|
||||
@@ -216,7 +212,10 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
}
|
||||
}
|
||||
|
||||
bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
|
||||
bool load_step(const char *path, Model *model, bool& is_cancel,
|
||||
double linear_defletion/*=0.003*/,
|
||||
double angle_defletion/*= 0.5*/,
|
||||
ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn, long& mesh_face_num)
|
||||
{
|
||||
bool cb_cancel = false;
|
||||
if (stepFn) {
|
||||
@@ -271,7 +270,7 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
stl.resize(namedSolids.size());
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, namedSolids.size()), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
|
||||
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, linear_defletion, false, angle_defletion, true);
|
||||
// QDS: calculate total number of the nodes and triangles
|
||||
int aNbNodes = 0;
|
||||
int aNbTriangles = 0;
|
||||
@@ -317,7 +316,7 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
}
|
||||
// QDS: copy triangles
|
||||
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
|
||||
Standard_Integer anId[3];
|
||||
Standard_Integer anId[3] = {};
|
||||
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
|
||||
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
|
||||
|
||||
@@ -344,6 +343,14 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
}
|
||||
});
|
||||
|
||||
if (mesh_face_num != -1) {
|
||||
for (size_t i = 0; i < stl.size(); i++) {
|
||||
// Test for overflow
|
||||
mesh_face_num += stl[i].stats.number_of_facets;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -388,4 +395,104 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
return true;
|
||||
}
|
||||
|
||||
Step::Step(fs::path path, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn):
|
||||
m_stepFn(stepFn),
|
||||
m_utf8Fn(isUtf8Fn)
|
||||
{
|
||||
m_path = path.string();
|
||||
m_app->NewDocument(TCollection_ExtendedString("BinXCAF"), m_doc);
|
||||
}
|
||||
|
||||
Step::Step(std::string path, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn) :
|
||||
m_path(path),
|
||||
m_stepFn(stepFn),
|
||||
m_utf8Fn(isUtf8Fn)
|
||||
{
|
||||
m_app->NewDocument(TCollection_ExtendedString("BinXCAF"), m_doc);
|
||||
}
|
||||
|
||||
bool Step::load()
|
||||
{
|
||||
if (!StepPreProcessor::isUtf8File(m_path.c_str()) && m_utf8Fn) {
|
||||
m_utf8Fn(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEPCAFControl_Reader reader;
|
||||
reader.SetNameMode(true);
|
||||
IFSelect_ReturnStatus stat = reader.ReadFile(m_path.c_str());
|
||||
if (stat != IFSelect_RetDone || !reader.Transfer(m_doc)) {
|
||||
m_app->Close(m_doc);
|
||||
return false;
|
||||
}
|
||||
m_shape_tool = XCAFDoc_DocumentTool::ShapeTool(m_doc->Main());
|
||||
TDF_LabelSequence topLevelShapes;
|
||||
m_shape_tool->GetFreeShapes(topLevelShapes);
|
||||
unsigned int id{ 1 };
|
||||
Standard_Integer topShapeLength = topLevelShapes.Length() + 1;
|
||||
for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) {
|
||||
getNamedSolids(TopLoc_Location{}, "", id, m_shape_tool, topLevelShapes.Value(iLabel), m_name_solids);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Step::clean_mesh_data()
|
||||
{
|
||||
for (const auto& name_solid : m_name_solids) {
|
||||
BRepTools::Clean(name_solid.solid);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Step::get_triangle_num(double linear_defletion, double angle_defletion)
|
||||
{
|
||||
unsigned int tri_num = 0;
|
||||
Handle(StepProgressIncdicator) progress = new StepProgressIncdicator(m_stop_mesh);
|
||||
clean_mesh_data();
|
||||
IMeshTools_Parameters param;
|
||||
param.Deflection = linear_defletion;
|
||||
param.Angle = angle_defletion;
|
||||
param.InParallel = true;
|
||||
for (int i = 0; i < m_name_solids.size(); ++i) {
|
||||
BRepMesh_IncrementalMesh mesh(m_name_solids[i].solid, param, progress->Start());
|
||||
for (TopExp_Explorer anExpSF(m_name_solids[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()) {
|
||||
tri_num += aTriangulation->NbTriangles();
|
||||
}
|
||||
}
|
||||
if (m_stop_mesh.load()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return tri_num;
|
||||
}
|
||||
|
||||
unsigned int Step::get_triangle_num_tbb(double linear_defletion, double angle_defletion)
|
||||
{
|
||||
unsigned int tri_num = 0;
|
||||
clean_mesh_data();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_name_solids.size()),
|
||||
[&](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||
unsigned int solids_tri_num = 0;
|
||||
BRepMesh_IncrementalMesh mesh(m_name_solids[i].solid, linear_defletion, false, angle_defletion, true);
|
||||
for (TopExp_Explorer anExpSF(m_name_solids[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()) {
|
||||
solids_tri_num += aTriangulation->NbTriangles();
|
||||
}
|
||||
}
|
||||
m_name_solids[i].tri_face_cout = solids_tri_num;
|
||||
}
|
||||
|
||||
});
|
||||
for (int i = 0; i < m_name_solids.size(); ++i) {
|
||||
tri_num += m_name_solids[i].tri_face_cout;
|
||||
}
|
||||
return tri_num;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
#ifndef slic3r_Format_STEP_hpp_
|
||||
#define slic3r_Format_STEP_hpp_
|
||||
|
||||
#include "XCAFDoc_DocumentTool.hxx"
|
||||
#include "XCAFApp_Application.hxx"
|
||||
#include "XCAFDoc_ShapeTool.hxx"
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <Message_ProgressIndicator.hxx>
|
||||
#include <atomic>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class TriangleMesh;
|
||||
class ModelObject;
|
||||
|
||||
@@ -16,8 +27,24 @@ const int LOAD_STEP_STAGE_UNIT_NUM = 5;
|
||||
typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
|
||||
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;
|
||||
|
||||
struct NamedSolid
|
||||
{
|
||||
NamedSolid(const TopoDS_Shape& s,
|
||||
const std::string& n) : solid{ s }, name{ n } {
|
||||
}
|
||||
const TopoDS_Shape solid;
|
||||
const std::string name;
|
||||
int tri_face_cout = 0;
|
||||
};
|
||||
|
||||
//QDS: Load an step file into a provided model.
|
||||
extern bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
extern bool load_step(const char *path, Model *model,
|
||||
bool& is_cancel,
|
||||
double linear_defletion = 0.003,
|
||||
double angle_defletion = 0.5,
|
||||
ImportStepProgressFn proFn = nullptr,
|
||||
StepIsUtf8Fn isUtf8Fn = nullptr,
|
||||
long& mesh_face_num = *(new long(-1)));
|
||||
|
||||
//QDS: 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.
|
||||
@@ -36,14 +63,50 @@ class StepPreProcessor {
|
||||
public:
|
||||
bool preprocess(const char* path, std::string &output_path);
|
||||
static bool isUtf8File(const char* path);
|
||||
private:
|
||||
static bool isUtf8(const std::string str);
|
||||
private:
|
||||
static bool isGBK(const std::string str);
|
||||
static int preNum(const unsigned char byte);
|
||||
//QDS: default is UTF8 for most step file.
|
||||
EncodedType m_encode_type = EncodedType::UTF8;
|
||||
};
|
||||
|
||||
class StepProgressIncdicator : public Message_ProgressIndicator
|
||||
{
|
||||
public:
|
||||
StepProgressIncdicator(std::atomic<bool>& stop_flag) : should_stop(stop_flag){}
|
||||
|
||||
Standard_Boolean UserBreak() override { return should_stop.load(); }
|
||||
|
||||
void Show(const Message_ProgressScope&, const Standard_Boolean) override {
|
||||
std::cout << "Progress: " << GetPosition() << "%" << std::endl;
|
||||
}
|
||||
private:
|
||||
std::atomic<bool>& should_stop;
|
||||
};
|
||||
|
||||
class Step
|
||||
{
|
||||
public:
|
||||
Step(fs::path path, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
Step(std::string path, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
bool load();
|
||||
unsigned int get_triangle_num(double linear_defletion, double angle_defletion);
|
||||
unsigned int get_triangle_num_tbb(double linear_defletion, double angle_defletion);
|
||||
void clean_mesh_data();
|
||||
|
||||
std::atomic<bool> m_stop_mesh;
|
||||
private:
|
||||
std::string m_path;
|
||||
ImportStepProgressFn m_stepFn;
|
||||
StepIsUtf8Fn m_utf8Fn;
|
||||
Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication();
|
||||
Handle(TDocStd_Document) m_doc;
|
||||
Handle(XCAFDoc_ShapeTool) m_shape_tool;
|
||||
std::vector<NamedSolid> m_name_solids;
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_STEP_hpp_ */
|
||||
|
||||
@@ -577,6 +577,8 @@ bool objparse(const char *path, ObjData &data)
|
||||
char buf[65536 * 2];
|
||||
size_t len = 0;
|
||||
size_t lenPrev = 0;
|
||||
size_t lineCount = 0;
|
||||
|
||||
while ((len = ::fread(buf + lenPrev, 1, 65536, pFile)) != 0) {
|
||||
len += lenPrev;
|
||||
size_t lastLine = 0;
|
||||
@@ -589,6 +591,13 @@ bool objparse(const char *path, ObjData &data)
|
||||
//FIXME check the return value and exit on error?
|
||||
// Will it break parsing of some obj files?
|
||||
obj_parseline(c, data);
|
||||
|
||||
/*for ml*/
|
||||
if (lineCount == 0) { data.ml_region = parsemlinfo(c, "region:");}
|
||||
if (lineCount == 1) { data.ml_name = parsemlinfo(c, "ml_name:"); }
|
||||
if (lineCount == 2) { data.ml_id = parsemlinfo(c, "ml_file_id:");}
|
||||
|
||||
++lineCount;
|
||||
lastLine = i + 1;
|
||||
}
|
||||
lenPrev = len - lastLine;
|
||||
@@ -607,6 +616,27 @@ bool objparse(const char *path, ObjData &data)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string parsemlinfo(const char* input, const char* condition) {
|
||||
const char* regionPtr = std::strstr(input, condition);
|
||||
|
||||
std::string regionContent = "";
|
||||
|
||||
if (regionPtr != nullptr) {
|
||||
regionPtr += std::strlen(condition);
|
||||
|
||||
while (*regionPtr == ' ' || *regionPtr == '\t') {
|
||||
++regionPtr;
|
||||
}
|
||||
|
||||
const char* endPtr = std::strchr(regionPtr, '\n');
|
||||
size_t length = (endPtr != nullptr) ? (endPtr - regionPtr) : std::strlen(regionPtr);
|
||||
|
||||
regionContent = std::string(regionPtr, length);
|
||||
}
|
||||
|
||||
return regionContent;
|
||||
}
|
||||
|
||||
bool mtlparse(const char *path, MtlData &data)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
@@ -664,6 +694,14 @@ bool objparse(std::istream &stream, ObjData &data)
|
||||
while (*c == ' ' || *c == '\t')
|
||||
++ c;
|
||||
obj_parseline(c, data);
|
||||
|
||||
/*for ml*/
|
||||
if (lastLine < 3) {
|
||||
data.ml_region = parsemlinfo(c, "region");
|
||||
data.ml_name = parsemlinfo(c, "ml_name");
|
||||
data.ml_id = parsemlinfo(c, "ml_file_id");
|
||||
}
|
||||
|
||||
lastLine = i + 1;
|
||||
}
|
||||
lenPrev = len - lastLine;
|
||||
|
||||
@@ -115,6 +115,10 @@ struct ObjData {
|
||||
|
||||
// List of faces, delimited by an ObjVertex with all members set to -1.
|
||||
std::vector<ObjVertex> vertices;
|
||||
|
||||
std::string ml_region;
|
||||
std::string ml_name;
|
||||
std::string ml_id;
|
||||
};
|
||||
|
||||
struct MtlData
|
||||
@@ -127,6 +131,8 @@ extern bool objparse(const char *path, ObjData &data);
|
||||
extern bool mtlparse(const char *path, MtlData &data);
|
||||
extern bool objparse(std::istream &stream, ObjData &data);
|
||||
|
||||
extern std::string parsemlinfo(const char* input, const char* condition);
|
||||
|
||||
extern bool objbinsave(const char *path, const ObjData &data);
|
||||
|
||||
extern bool objbinload(const char *path, ObjData &data);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <stdexcept>
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/assign.hpp>
|
||||
#include <boost/bimap.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -46,6 +48,11 @@ namespace pt = boost::property_tree;
|
||||
#include <Eigen/Dense>
|
||||
#include "miniz_extension.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "EmbossShape.hpp"
|
||||
#include "ExPolygonSerialize.hpp"
|
||||
#include "NSVGUtils.hpp"
|
||||
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
|
||||
@@ -130,6 +137,9 @@ const std::string QDT_APPLICATION_TAG = "Application";
|
||||
const std::string QDT_MAKERLAB_TAG = "MakerLab";
|
||||
const std::string QDT_MAKERLAB_VERSION_TAG = "MakerLabVersion";
|
||||
|
||||
const std::string QDT_MAKERLAB_NAME = "MakerLab";
|
||||
const std::string QDT_MAKERLAB_REGION = "MakerLabRegion";
|
||||
const std::string QDT_MAKERLAB_ID = "MakerLabFileId";
|
||||
|
||||
const std::string QDT_PROFILE_TITLE_TAG = "ProfileTitle";
|
||||
const std::string QDT_PROFILE_COVER_TAG = "ProfileCover";
|
||||
@@ -163,6 +173,7 @@ const std::string QDS_MODEL_CONFIG_RELS_FILE = "Metadata/_rels/model_settings.co
|
||||
const std::string SLICE_INFO_CONFIG_FILE = "Metadata/slice_info.config";
|
||||
const std::string QDS_LAYER_HEIGHTS_PROFILE_FILE = "Metadata/layer_heights_profile.txt";
|
||||
const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/layer_config_ranges.xml";
|
||||
const std::string BRIM_EAR_POINTS_FILE = "Metadata/brim_ear_points.txt";
|
||||
/*const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
|
||||
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";*/
|
||||
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/custom_gcode_per_layer.xml";
|
||||
@@ -332,6 +343,40 @@ static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
|
||||
static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
|
||||
static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
|
||||
|
||||
// Store / load of TextConfiguration
|
||||
static constexpr const char *TEXT_DATA_ATTR = "text";
|
||||
// TextConfiguration::EmbossStyle
|
||||
static constexpr const char *STYLE_NAME_ATTR = "style_name";
|
||||
static constexpr const char *FONT_DESCRIPTOR_ATTR = "font_descriptor";
|
||||
static constexpr const char *FONT_DESCRIPTOR_TYPE_ATTR = "font_descriptor_type";
|
||||
|
||||
// TextConfiguration::FontProperty
|
||||
static constexpr const char *CHAR_GAP_ATTR = "char_gap";
|
||||
static constexpr const char *LINE_GAP_ATTR = "line_gap";
|
||||
static constexpr const char *LINE_HEIGHT_ATTR = "line_height";
|
||||
static constexpr const char *BOLDNESS_ATTR = "boldness";
|
||||
static constexpr const char *SKEW_ATTR = "skew";
|
||||
static constexpr const char *PER_GLYPH_ATTR = "per_glyph";
|
||||
static constexpr const char *HORIZONTAL_ALIGN_ATTR = "horizontal";
|
||||
static constexpr const char *VERTICAL_ALIGN_ATTR = "vertical";
|
||||
static constexpr const char *COLLECTION_NUMBER_ATTR = "collection";
|
||||
|
||||
static constexpr const char *FONT_FAMILY_ATTR = "family";
|
||||
static constexpr const char *FONT_FACE_NAME_ATTR = "face_name";
|
||||
static constexpr const char *FONT_STYLE_ATTR = "style";
|
||||
static constexpr const char *FONT_WEIGHT_ATTR = "weight";
|
||||
|
||||
// Store / load of EmbossShape
|
||||
static constexpr const char *SHAPE_TAG = "slic3rpe:shape";
|
||||
static constexpr const char *SHAPE_SCALE_ATTR = "scale";
|
||||
static constexpr const char *UNHEALED_ATTR = "unhealed";
|
||||
static constexpr const char *SVG_FILE_PATH_ATTR = "filepath";
|
||||
static constexpr const char *SVG_FILE_PATH_IN_3MF_ATTR = "filepath3mf";
|
||||
|
||||
// EmbossProjection
|
||||
static constexpr const char *DEPTH_ATTR = "depth";
|
||||
static constexpr const char *USE_SURFACE_ATTR = "use_surface";
|
||||
// static constexpr const char *FIX_TRANSFORMATION_ATTR = "transform";
|
||||
|
||||
const unsigned int QDS_VALID_OBJECT_TYPES_COUNT = 2;
|
||||
const char* QDS_VALID_OBJECT_TYPES[] =
|
||||
@@ -719,7 +764,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
RepairedMeshErrors mesh_stats;
|
||||
ModelVolumeType part_type;
|
||||
TextInfo text_info;
|
||||
|
||||
std::optional<EmbossShape> shape_configuration;
|
||||
VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id, ModelVolumeType type = ModelVolumeType::MODEL_PART)
|
||||
: first_triangle_id(first_triangle_id)
|
||||
, last_triangle_id(last_triangle_id)
|
||||
@@ -770,8 +815,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
//typedef std::map<Id, Geometry> IdToGeometryMap;
|
||||
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
|
||||
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
|
||||
typedef std::map<int, BrimPoints> IdToBrimPointsMap;
|
||||
/*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
||||
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;*/
|
||||
using PathToEmbossShapeFileMap = std::map<std::string, std::shared_ptr<std::string>>;
|
||||
|
||||
struct ObjectImporter
|
||||
{
|
||||
@@ -836,7 +883,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
bool extract_object_model()
|
||||
{
|
||||
mz_zip_archive archive;
|
||||
mz_zip_archive_file_stat stat;
|
||||
//mz_zip_archive_file_stat stat;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
if (!open_zip_reader(&archive, zip_path)) {
|
||||
@@ -964,8 +1011,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
IdToCutObjectInfoMap m_cut_object_infos;
|
||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
IdToBrimPointsMap m_brim_ear_points;
|
||||
/*IdToSlaSupportPointsMap m_sla_support_points;
|
||||
IdToSlaDrainHolesMap m_sla_drain_holes;*/
|
||||
PathToEmbossShapeFileMap m_path_to_emboss_shape_files;
|
||||
std::string m_curr_metadata_name;
|
||||
std::string m_curr_characters;
|
||||
std::string m_name;
|
||||
@@ -1019,6 +1068,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
// add backup & restore logic
|
||||
bool _load_model_from_file(std::string filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Import3mfProgressFn proFn = nullptr,
|
||||
QDTProject* project = nullptr, int plate_id = 0);
|
||||
bool _is_svg_shape_file(const std::string &filename) const;
|
||||
bool _extract_from_archive(mz_zip_archive& archive, std::string const & path, std::function<bool (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)>, bool restore = false);
|
||||
bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
|
||||
bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
|
||||
@@ -1028,6 +1078,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_brim_ear_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
|
||||
void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
|
||||
@@ -1039,6 +1090,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
void _extract_auxiliary_file_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
|
||||
void _extract_file_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_embossed_svg_shape_file(const std::string &filename, mz_zip_archive &archive, const mz_zip_archive_file_stat &stat);
|
||||
|
||||
// handlers to parse the .model file
|
||||
void _handle_start_model_xml_element(const char* name, const char** attributes);
|
||||
@@ -1094,6 +1146,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
|
||||
bool _handle_end_metadata();
|
||||
|
||||
bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes);
|
||||
|
||||
bool _create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
|
||||
|
||||
void _apply_transform(ModelInstance& instance, const Transform3d& transform);
|
||||
@@ -1225,6 +1279,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_objects_metadata.clear();
|
||||
m_layer_heights_profiles.clear();
|
||||
m_layer_config_ranges.clear();
|
||||
m_brim_ear_points.clear();
|
||||
//m_sla_support_points.clear();
|
||||
m_curr_metadata_name.clear();
|
||||
m_curr_characters.clear();
|
||||
@@ -1720,6 +1775,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
// extract slic3r layer config ranges file
|
||||
_extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, BRIM_EAR_POINTS_FILE)) {
|
||||
// extract slic3r config file
|
||||
_extract_brim_ear_points_from_archive(archive, stat);
|
||||
}
|
||||
//QDS: disable SLA related files currently
|
||||
/*else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE)) {
|
||||
// extract sla support points file
|
||||
@@ -1769,6 +1828,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
add_error("Archive does not contain a valid model config");
|
||||
return false;
|
||||
}
|
||||
} else if (_is_svg_shape_file(name)) {
|
||||
_extract_embossed_svg_shape_file(name, archive, stat);
|
||||
}
|
||||
else if (!dont_load_config && boost::algorithm::iequals(name, SLICE_INFO_CONFIG_FILE)) {
|
||||
m_parsing_slice_info = true;
|
||||
@@ -1900,6 +1961,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
if (obj_layer_config_ranges != m_layer_config_ranges.end())
|
||||
model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second);
|
||||
|
||||
IdToBrimPointsMap::iterator obj_brim_points = m_brim_ear_points.find(object.second + 1);
|
||||
if (obj_brim_points != m_brim_ear_points.end())
|
||||
model_object->brim_points = std::move(obj_brim_points->second);
|
||||
|
||||
// m_sla_support_points are indexed by a 1 based model object index.
|
||||
/*IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
|
||||
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
|
||||
@@ -2167,6 +2232,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _QDS_3MF_Importer::_is_svg_shape_file(const std::string &name) const {
|
||||
return boost::starts_with(name, MODEL_FOLDER) && boost::ends_with(name, ".svg");
|
||||
}
|
||||
|
||||
bool _QDS_3MF_Importer::_extract_from_archive(mz_zip_archive& archive, std::string const & path, std::function<bool (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)> extract, bool restore)
|
||||
{
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
@@ -2710,6 +2779,78 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _QDS_3MF_Importer::_extract_brim_ear_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||
{
|
||||
if (stat.m_uncomp_size > 0) {
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading brim ear points data to buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.back() == '\n')
|
||||
buffer.pop_back();
|
||||
|
||||
std::vector<std::string> objects;
|
||||
boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
|
||||
|
||||
// Info on format versioning - see qds_3mf.hpp
|
||||
int version = 0;
|
||||
std::string key("brim_points_format_version=");
|
||||
if (!objects.empty() && objects[0].find(key) != std::string::npos) {
|
||||
objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
|
||||
version = std::stoi(objects[0]);
|
||||
objects.erase(objects.begin()); // pop the header
|
||||
}
|
||||
|
||||
for (const std::string& object : objects) {
|
||||
std::vector<std::string> object_data;
|
||||
boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
|
||||
|
||||
if (object_data.size() != 2) {
|
||||
add_error("Error while reading object data");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> object_data_id;
|
||||
boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
|
||||
if (object_data_id.size() != 2) {
|
||||
add_error("Error while reading object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
int object_id = std::atoi(object_data_id[1].c_str());
|
||||
if (object_id == 0) {
|
||||
add_error("Found invalid object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
IdToBrimPointsMap::iterator object_item = m_brim_ear_points.find(object_id);
|
||||
if (object_item != m_brim_ear_points.end()) {
|
||||
add_error("Found duplicated brim ear points");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> object_data_points;
|
||||
boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
|
||||
|
||||
std::vector<BrimPoint> brim_ear_points;
|
||||
if (version == 0) {
|
||||
for (unsigned int i=0; i<object_data_points.size(); i+=4)
|
||||
brim_ear_points.emplace_back(float(std::atof(object_data_points[i+0].c_str())),
|
||||
float(std::atof(object_data_points[i+1].c_str())),
|
||||
float(std::atof(object_data_points[i+2].c_str())),
|
||||
float(std::atof(object_data_points[i+3].c_str())));
|
||||
}
|
||||
|
||||
if (!brim_ear_points.empty())
|
||||
m_brim_ear_points.insert({ object_id, brim_ear_points });
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
void _QDS_3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||
{
|
||||
@@ -2878,6 +3019,30 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
}*/
|
||||
|
||||
void _QDS_3MF_Importer::_extract_embossed_svg_shape_file(const std::string &filename, mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
|
||||
{
|
||||
assert(m_path_to_emboss_shape_files.find(filename) == m_path_to_emboss_shape_files.end());
|
||||
auto file = std::make_unique<std::string>(stat.m_uncomp_size, '\0');
|
||||
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void *) file->data(), stat.m_uncomp_size, 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading svg shape for emboss");
|
||||
return;
|
||||
}
|
||||
|
||||
// store for case svg is loaded before volume
|
||||
m_path_to_emboss_shape_files[filename] = std::move(file);
|
||||
|
||||
// find embossed volume, for case svg is loaded after volume
|
||||
for (const ModelObject *object : m_model->objects)
|
||||
for (ModelVolume *volume : object->volumes) {
|
||||
std::optional<EmbossShape> &es = volume->emboss_shape;
|
||||
if (!es.has_value()) continue;
|
||||
std::optional<EmbossShape::SvgFile> &svg = es->svg_file;
|
||||
if (!svg.has_value()) continue;
|
||||
if (filename.compare(svg->path_in_3mf) == 0) svg->file_data = m_path_to_emboss_shape_files[filename];
|
||||
}
|
||||
}
|
||||
|
||||
void _QDS_3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
|
||||
{
|
||||
//QDS: add plate tree related logic
|
||||
@@ -3088,7 +3253,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
res = _handle_start_assemble_item(attributes, num_attributes);
|
||||
else if (::strcmp(TEXT_INFO_TAG, name) == 0)
|
||||
res = _handle_start_text_info_item(attributes, num_attributes);
|
||||
|
||||
else if (::strcmp(SHAPE_TAG, name) == 0)
|
||||
res = _handle_start_shape_configuration(attributes, num_attributes);
|
||||
if (!res)
|
||||
_stop_xml_parser();
|
||||
}
|
||||
@@ -3641,6 +3807,40 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Definition of read/write method for EmbossShape
|
||||
static void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume &volume, mz_zip_archive &archive);
|
||||
static std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes);
|
||||
|
||||
bool _QDS_3MF_Importer::_handle_start_shape_configuration(const char **attributes, unsigned int num_attributes)
|
||||
{
|
||||
IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
|
||||
if (object == m_objects_metadata.end()) {
|
||||
add_error("Can not assign volume mesh to a valid object");
|
||||
return false;
|
||||
}
|
||||
auto &volumes = object->second.volumes;
|
||||
if (volumes.empty()) {
|
||||
add_error("Can not assign mesh to a valid volume");
|
||||
return false;
|
||||
}
|
||||
ObjectMetadata::VolumeMetadata &volume = volumes.back();
|
||||
volume.shape_configuration = read_emboss_shape(attributes, num_attributes);
|
||||
if (!volume.shape_configuration.has_value()) return false;
|
||||
|
||||
// Fill svg file content into shape_configuration
|
||||
std::optional<EmbossShape::SvgFile> &svg = volume.shape_configuration->svg_file;
|
||||
if (!svg.has_value()) return true; // do not contain svg file
|
||||
|
||||
const std::string &path = svg->path_in_3mf;
|
||||
if (path.empty()) return true; // do not contain svg file
|
||||
|
||||
auto it = m_path_to_emboss_shape_files.find(path);
|
||||
if (it == m_path_to_emboss_shape_files.end()) return true; // svg file is not loaded yet
|
||||
|
||||
svg->file_data = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _QDS_3MF_Importer::_create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
|
||||
{
|
||||
static const unsigned int MAX_RECURSIONS = 10;
|
||||
@@ -4205,7 +4405,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
|
||||
ObjectMetadata::VolumeMetadata &volume = object->second.volumes[m_curr_config.volume_id];
|
||||
|
||||
TextInfo text_info;
|
||||
text_info.m_text = xml_unescape(qds_get_attribute_value_string(attributes, num_attributes, TEXT_ATTR));
|
||||
text_info.m_font_name = qds_get_attribute_value_string(attributes, num_attributes, FONT_NAME_ATTR);
|
||||
@@ -4293,9 +4492,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
} else if (boost::starts_with(type, "http://schemas.openxmlformats.org/") && boost::ends_with(type, "thumbnail")) {
|
||||
if (boost::algorithm::ends_with(path, ".png"))
|
||||
m_thumbnail_path = path;
|
||||
} else if (boost::starts_with(type, "http://schemas.qidilab.com/") && boost::ends_with(type, "cover-thumbnail-middle")) {
|
||||
} else if (boost::starts_with(type, "http://schemas.qiditech.com/") && boost::ends_with(type, "cover-thumbnail-middle")) {
|
||||
m_thumbnail_middle = path;
|
||||
} else if (boost::starts_with(type, "http://schemas.qidilab.com/") && boost::ends_with(type, "cover-thumbnail-small")) {
|
||||
} else if (boost::starts_with(type, "http://schemas.qiditech.com/") && boost::ends_with(type, "cover-thumbnail-small")) {
|
||||
m_thumbnail_small = path;
|
||||
}
|
||||
return true;
|
||||
@@ -4510,7 +4709,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
|
||||
volume->set_type(volume_data->part_type);
|
||||
|
||||
if (auto &es = volume_data->shape_configuration; es.has_value())
|
||||
volume->emboss_shape = std::move(es);
|
||||
if (!volume_data->text_info.m_text.empty())
|
||||
volume->set_text_info(volume_data->text_info);
|
||||
|
||||
@@ -5279,7 +5479,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
bool save_model_to_file(StoreParams& store_params);
|
||||
// add backup logic
|
||||
bool save_object_mesh(const std::string& temp_path, ModelObject const & object, int obj_id);
|
||||
|
||||
static void add_transformation(std::stringstream &stream, const Transform3d &tr);
|
||||
private:
|
||||
//QDS: add plate data related logic
|
||||
bool _save_model_to_file(const std::string& filename,
|
||||
@@ -5317,6 +5517,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const;
|
||||
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_brim_ear_points_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
|
||||
@@ -5725,6 +5926,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_add_brim_ear_points_file_to_archive(archive, model)) {
|
||||
close_zip_writer(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
// QDS progress point
|
||||
/*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_SUPPORT\n");
|
||||
if (proFn) {
|
||||
@@ -6076,18 +6282,18 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
if (data._3mf_printer_thumbnail_middle.empty()) {
|
||||
stream << " <Relationship Target=\"/Metadata/plate_1.png"
|
||||
<< "\" Id=\"rel-4\" Type=\"http://schemas.qidilab.com/package/2021/cover-thumbnail-middle\"/>\n";
|
||||
<< "\" Id=\"rel-4\" Type=\"http://schemas.qiditech.com/package/2021/cover-thumbnail-middle\"/>\n";
|
||||
} else {
|
||||
stream << " <Relationship Target=\"/" << xml_escape(data._3mf_printer_thumbnail_middle)
|
||||
<< "\" Id=\"rel-4\" Type=\"http://schemas.qidilab.com/package/2021/cover-thumbnail-middle\"/>\n";
|
||||
<< "\" Id=\"rel-4\" Type=\"http://schemas.qiditech.com/package/2021/cover-thumbnail-middle\"/>\n";
|
||||
}
|
||||
|
||||
if (data._3mf_printer_thumbnail_small.empty()) {
|
||||
stream << "<Relationship Target=\"/Metadata/plate_1_small.png"
|
||||
<< "\" Id=\"rel-5\" Type=\"http://schemas.qidilab.com/package/2021/cover-thumbnail-small\"/>\n";
|
||||
<< "\" Id=\"rel-5\" Type=\"http://schemas.qiditech.com/package/2021/cover-thumbnail-small\"/>\n";
|
||||
} else {
|
||||
stream << " <Relationship Target=\"/" << xml_escape(data._3mf_printer_thumbnail_small)
|
||||
<< "\" Id=\"rel-5\" Type=\"http://schemas.qidilab.com/package/2021/cover-thumbnail-small\"/>\n";
|
||||
<< "\" Id=\"rel-5\" Type=\"http://schemas.qiditech.com/package/2021/cover-thumbnail-small\"/>\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -6098,11 +6304,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
thumbnail_file_str = (boost::format("Metadata/plate_%1%.png") % (export_plate_idx + 1)).str();
|
||||
stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
|
||||
<< "\" Id=\"rel-4\" Type=\"http://schemas.qidilab.com/package/2021/cover-thumbnail-middle\"/>\n";
|
||||
<< "\" Id=\"rel-4\" Type=\"http://schemas.qiditech.com/package/2021/cover-thumbnail-middle\"/>\n";
|
||||
|
||||
thumbnail_file_str = (boost::format("Metadata/plate_%1%_small.png") % (export_plate_idx + 1)).str();
|
||||
stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
|
||||
<< "\" Id=\"rel-5\" Type=\"http://schemas.qidilab.com/package/2021/cover-thumbnail-small\"/>\n";
|
||||
<< "\" Id=\"rel-5\" Type=\"http://schemas.qiditech.com/package/2021/cover-thumbnail-small\"/>\n";
|
||||
}
|
||||
}
|
||||
else if (targets.empty()) {
|
||||
@@ -6182,7 +6388,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
std::stringstream stream;
|
||||
reset_stream(stream);
|
||||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:QIDIStudio=\"http://schemas.qidilab.com/package/2021\"";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:QIDIStudio=\"http://schemas.qiditech.com/package/2021\"";
|
||||
if (m_production_ext)
|
||||
stream << " xmlns:p=\"http://schemas.microsoft.com/3dmanufacturing/production/2015/06\" requiredextensions=\"p\"";
|
||||
stream << ">\n";
|
||||
@@ -6259,6 +6465,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
metadata_item_map[QDT_MAKERLAB_VERSION_TAG] = xml_escape(model.mk_version);
|
||||
BOOST_LOG_TRIVIAL(info) << "saved mk_version " << model.mk_version;
|
||||
}
|
||||
|
||||
/*for ml*/
|
||||
if (model.mk_name.empty() && !model.makerlab_name.empty()) {
|
||||
metadata_item_map[QDT_MAKERLAB_NAME] = model.makerlab_name;
|
||||
metadata_item_map[QDT_MAKERLAB_REGION] = model.makerlab_region;
|
||||
metadata_item_map[QDT_MAKERLAB_ID] = model.makerlab_id;
|
||||
}
|
||||
|
||||
if (!model.md_name.empty()) {
|
||||
for (unsigned int i = 0; i < model.md_name.size(); i++)
|
||||
{
|
||||
@@ -6768,6 +6982,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return flush(output_buffer, true);
|
||||
}
|
||||
|
||||
void _QDS_3MF_Exporter::add_transformation(std::stringstream &stream, const Transform3d &tr)
|
||||
{
|
||||
for (unsigned c = 0; c < 4; ++c) {
|
||||
for (unsigned r = 0; r < 3; ++r) {
|
||||
stream << tr(r, c);
|
||||
if (r != 2 || c != 3)
|
||||
stream << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _QDS_3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const
|
||||
{
|
||||
// This happens for empty projects
|
||||
@@ -6788,13 +7013,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
if (!item.path.empty())
|
||||
stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path);
|
||||
stream << "\" " << TRANSFORM_ATTR << "=\"";
|
||||
for (unsigned c = 0; c < 4; ++c) {
|
||||
for (unsigned r = 0; r < 3; ++r) {
|
||||
stream << item.transform(r, c);
|
||||
if (r != 2 || c != 3)
|
||||
stream << " ";
|
||||
}
|
||||
}
|
||||
add_transformation(stream, item.transform);
|
||||
stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
|
||||
}
|
||||
|
||||
@@ -6838,6 +7057,40 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _QDS_3MF_Exporter::_add_brim_ear_points_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
std::string out = "";
|
||||
char buffer[1024];
|
||||
|
||||
unsigned int count = 0;
|
||||
for (const ModelObject* object : model.objects) {
|
||||
++count;
|
||||
const BrimPoints& brim_points = object->brim_points;
|
||||
if (!brim_points.empty()) {
|
||||
sprintf(buffer, "object_id=%d|", count);
|
||||
out += buffer;
|
||||
|
||||
// Store the layer height profile as a single space separated list.
|
||||
for (size_t i = 0; i < brim_points.size(); ++i) {
|
||||
sprintf(buffer, (i==0 ? "%f %f %f %f" : " %f %f %f %f"), brim_points[i].pos(0), brim_points[i].pos(1), brim_points[i].pos(2), brim_points[i].head_front_radius);
|
||||
out += buffer;
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!out.empty()) {
|
||||
// Adds version header at the beginning:
|
||||
out = std::string("brim_points_format_version=") + std::to_string(brim_points_format_version) + std::string("\n") + out;
|
||||
|
||||
if (!mz_zip_writer_add_mem(&archive, BRIM_EAR_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
|
||||
add_error("Unable to add brim ear points file to archive");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _QDS_3MF_Exporter::_add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
std::string out = "";
|
||||
@@ -7188,6 +7441,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
|
||||
}
|
||||
|
||||
if (const std::optional<EmbossShape> &es = volume->emboss_shape; es.has_value())
|
||||
to_xml(stream, *es, *volume, archive);
|
||||
|
||||
const TextInfo &text_info = volume->get_text_info();
|
||||
if (!text_info.m_text.empty())
|
||||
_add_text_info_to_archive(stream, text_info);
|
||||
@@ -7307,7 +7563,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
if (!m_skip_model && instance_size > 0)
|
||||
{
|
||||
for (unsigned int j = 0; j < instance_size; ++j)
|
||||
for (int j = 0; j < instance_size; ++j)
|
||||
{
|
||||
stream << " <" << INSTANCE_TAG << ">\n";
|
||||
int obj_id = plate_data->objects_and_instances[j].first;
|
||||
@@ -7352,7 +7608,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
|
||||
// write model rels
|
||||
if (save_gcode)
|
||||
_add_relationships_file_to_archive(archive, QDS_MODEL_CONFIG_RELS_FILE, gcode_paths, {"http://schemas.qidilab.com/package/2021/gcode"}, Slic3r::PackingTemporaryData(), export_plate_idx);
|
||||
_add_relationships_file_to_archive(archive, QDS_MODEL_CONFIG_RELS_FILE, gcode_paths, {"http://schemas.qiditech.com/package/2021/gcode"}, Slic3r::PackingTemporaryData(), export_plate_idx);
|
||||
|
||||
if (!m_skip_model) {
|
||||
//QDS: store assemble related info
|
||||
@@ -8290,4 +8546,148 @@ SaveObjectGaurd::~SaveObjectGaurd()
|
||||
_QDS_Backup_Manager::get().pop_object_gaurd();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Conversion with bidirectional map
|
||||
// F .. first, S .. second
|
||||
template<typename F, typename S> F bimap_cvt(const boost::bimap<F, S> &bmap, S s, const F &def_value)
|
||||
{
|
||||
const auto &map = bmap.right;
|
||||
auto found_item = map.find(s);
|
||||
|
||||
// only for back and forward compatibility
|
||||
assert(found_item != map.end());
|
||||
if (found_item == map.end()) return def_value;
|
||||
|
||||
return found_item->second;
|
||||
}
|
||||
|
||||
template<typename F, typename S> S bimap_cvt(const boost::bimap<F, S> &bmap, F f, const S &def_value)
|
||||
{
|
||||
const auto &map = bmap.left;
|
||||
auto found_item = map.find(f);
|
||||
|
||||
// only for back and forward compatibility
|
||||
assert(found_item != map.end());
|
||||
if (found_item == map.end()) return def_value;
|
||||
|
||||
return found_item->second;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
Transform3d create_fix(const std::optional<Transform3d> &prev, const ModelVolume &volume)
|
||||
{
|
||||
// IMPROVE: check if volume was modified (translated, rotated OR scaled)
|
||||
// when no change do not calculate transformation only store original fix matrix
|
||||
|
||||
// Create transformation used after load actual stored volume
|
||||
// Orca: do not bake volume transformation into meshes
|
||||
// const Transform3d &actual_trmat = volume.get_matrix();
|
||||
const Transform3d &actual_trmat = Transform3d::Identity();
|
||||
|
||||
const auto &vertices = volume.mesh().its.vertices;
|
||||
Vec3d min = actual_trmat * vertices.front().cast<double>();
|
||||
Vec3d max = min;
|
||||
for (const Vec3f &v : vertices) {
|
||||
Vec3d vd = actual_trmat * v.cast<double>();
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
if (min[i] > vd[i]) min[i] = vd[i];
|
||||
if (max[i] < vd[i]) max[i] = vd[i];
|
||||
}
|
||||
}
|
||||
Vec3d center = (max + min) / 2;
|
||||
Transform3d post_trmat = Transform3d::Identity();
|
||||
post_trmat.translate(center);
|
||||
|
||||
Transform3d fix_trmat = actual_trmat.inverse() * post_trmat;
|
||||
if (!prev.has_value()) return fix_trmat;
|
||||
|
||||
// check whether fix somehow differ previous
|
||||
if (fix_trmat.isApprox(Transform3d::Identity(), 1e-5)) return *prev;
|
||||
|
||||
return *prev * fix_trmat;
|
||||
}
|
||||
|
||||
bool to_xml(std::stringstream &stream, const EmbossShape::SvgFile &svg, const ModelVolume &volume, mz_zip_archive &archive)
|
||||
{
|
||||
if (svg.path_in_3mf.empty())
|
||||
return true; // EmbossedText OR unwanted store .svg file into .3mf (protection of copyRight)
|
||||
|
||||
if (!svg.path.empty())
|
||||
stream << SVG_FILE_PATH_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(svg.path) << "\" ";
|
||||
stream << SVG_FILE_PATH_IN_3MF_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(svg.path_in_3mf) << "\" ";
|
||||
|
||||
std::shared_ptr<std::string> file_data = svg.file_data;
|
||||
assert(file_data != nullptr);
|
||||
if (file_data == nullptr && !svg.path.empty()) file_data = read_from_disk(svg.path);
|
||||
if (file_data == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Can't write svg file no filedata";
|
||||
return false;
|
||||
}
|
||||
const std::string &file_data_str = *file_data;
|
||||
|
||||
return mz_zip_writer_add_mem(&archive, svg.path_in_3mf.c_str(), (const void *) file_data_str.c_str(), file_data_str.size(), MZ_DEFAULT_COMPRESSION);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume &volume, mz_zip_archive &archive)
|
||||
{
|
||||
stream << " <" << SHAPE_TAG << " ";
|
||||
if (es.svg_file.has_value())
|
||||
if (!to_xml(stream, *es.svg_file, volume, archive)) BOOST_LOG_TRIVIAL(warning) << "Can't write svg file defiden embossed shape into 3mf";
|
||||
|
||||
stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" ";
|
||||
|
||||
if (!es.final_shape.is_healed) stream << UNHEALED_ATTR << "=\"" << 1 << "\" ";
|
||||
|
||||
// projection
|
||||
const EmbossProjection &p = es.projection;
|
||||
stream << DEPTH_ATTR << "=\"" << p.depth << "\" ";
|
||||
if (p.use_surface) stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" ";
|
||||
|
||||
// FIX of baked transformation
|
||||
Transform3d fix = create_fix(es.fix_3mf_tr, volume);
|
||||
stream << TRANSFORM_ATTR << "=\"";
|
||||
_QDS_3MF_Exporter::add_transformation(stream, fix);
|
||||
stream << "\" ";
|
||||
|
||||
stream << "/>\n"; // end SHAPE_TAG
|
||||
}
|
||||
|
||||
std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes)
|
||||
{
|
||||
double scale = qds_get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
|
||||
int unhealed = qds_get_attribute_value_int(attributes, num_attributes, UNHEALED_ATTR);
|
||||
bool is_healed = unhealed != 1;
|
||||
|
||||
EmbossProjection projection;
|
||||
projection.depth = qds_get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);
|
||||
if (is_approx(projection.depth, 0.)) projection.depth = 10.;
|
||||
|
||||
int use_surface = qds_get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
|
||||
if (use_surface == 1) projection.use_surface = true;
|
||||
|
||||
std::optional<Transform3d> fix_tr_mat;
|
||||
std::string fix_tr_mat_str = qds_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR);
|
||||
if (!fix_tr_mat_str.empty()) { fix_tr_mat = qds_get_transform_from_3mf_specs_string(fix_tr_mat_str); }
|
||||
|
||||
std::string file_path = qds_get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_ATTR);
|
||||
std::string file_path_3mf = qds_get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_IN_3MF_ATTR);
|
||||
|
||||
// MayBe: store also shapes to not store svg
|
||||
// But be carefull curve will be lost -> scale will not change sampling
|
||||
// shapes could be loaded from SVG
|
||||
ExPolygonsWithIds shapes;
|
||||
// final shape could be calculated from shapes
|
||||
HealedExPolygons final_shape;
|
||||
final_shape.is_healed = is_healed;
|
||||
|
||||
EmbossShape::SvgFile svg{file_path, file_path_3mf};
|
||||
return EmbossShape{std::move(shapes), std::move(final_shape), scale, std::move(projection), std::move(fix_tr_mat), std::move(svg)};
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -141,6 +141,10 @@ inline bool operator & (SaveStrategy & lhs, SaveStrategy rhs)
|
||||
return ((static_cast<T>(lhs) & static_cast<T>(rhs))) == static_cast<T>(rhs);
|
||||
}
|
||||
|
||||
enum {
|
||||
brim_points_format_version = 0
|
||||
};
|
||||
|
||||
enum class LoadStrategy
|
||||
{
|
||||
Default = 0,
|
||||
|
||||
Reference in New Issue
Block a user