mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-18 07:05:56 +08:00
Store shape as svg into 3mf
Without obfuscation of svg.
This commit is contained in:
parent
f36330df4f
commit
480c571499
@ -73,7 +73,7 @@ struct EmbossShape
|
|||||||
ExPolygonsWithIds shapes_with_ids;
|
ExPolygonsWithIds shapes_with_ids;
|
||||||
|
|
||||||
// scale of shape, multiplier to get 3d point in mm from integer shape
|
// scale of shape, multiplier to get 3d point in mm from integer shape
|
||||||
double scale = 1.;
|
double scale = SCALING_FACTOR;
|
||||||
|
|
||||||
// Define how to emboss shape
|
// Define how to emboss shape
|
||||||
EmbossProjection projection;
|
EmbossProjection projection;
|
||||||
@ -98,6 +98,9 @@ struct EmbossShape
|
|||||||
// Loaded svg file data.
|
// Loaded svg file data.
|
||||||
// !!! It is not serialized on undo/redo stack
|
// !!! It is not serialized on undo/redo stack
|
||||||
std::shared_ptr<NSVGimage> image = nullptr;
|
std::shared_ptr<NSVGimage> image = nullptr;
|
||||||
|
|
||||||
|
// Loaded string data from file
|
||||||
|
std::shared_ptr<char[]> file_data = nullptr;
|
||||||
};
|
};
|
||||||
SvgFile svg_file;
|
SvgFile svg_file;
|
||||||
|
|
||||||
|
@ -39,6 +39,8 @@ namespace pt = boost::property_tree;
|
|||||||
#include "EmbossShape.hpp"
|
#include "EmbossShape.hpp"
|
||||||
#include "ExPolygonSerialize.hpp"
|
#include "ExPolygonSerialize.hpp"
|
||||||
|
|
||||||
|
#include "NSVGUtils.hpp"
|
||||||
|
|
||||||
#include <fast_float/fast_float.h>
|
#include <fast_float/fast_float.h>
|
||||||
|
|
||||||
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
|
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
|
||||||
@ -173,9 +175,9 @@ static constexpr const char *FONT_WEIGHT_ATTR = "weight";
|
|||||||
|
|
||||||
// Store / load of EmbossShape
|
// Store / load of EmbossShape
|
||||||
static constexpr const char *SHAPE_TAG = "slic3rpe:shape";
|
static constexpr const char *SHAPE_TAG = "slic3rpe:shape";
|
||||||
static constexpr const char *SHAPE_EXPOLYS_ATTR = "expolygons";
|
|
||||||
static constexpr const char *SHAPE_SCALE_ATTR = "scale";
|
static constexpr const char *SHAPE_SCALE_ATTR = "scale";
|
||||||
static constexpr const char *SVG_FILE_PATH_ATTR = "filepath";
|
static constexpr const char *SVG_FILE_PATH_ATTR = "filepath";
|
||||||
|
static constexpr const char *SVG_FILE_PATH_IN_3MF_ATTR = "filepath3mf";
|
||||||
|
|
||||||
// EmbossProjection
|
// EmbossProjection
|
||||||
static constexpr const char *DEPTH_ATTR = "depth";
|
static constexpr const char *DEPTH_ATTR = "depth";
|
||||||
@ -467,7 +469,7 @@ namespace Slic3r {
|
|||||||
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
|
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
|
||||||
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
||||||
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;
|
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;
|
||||||
|
using PathToEmbossShapeFileMap = std::map<std::string, std::shared_ptr<char[]>>;
|
||||||
// Version of the 3mf file
|
// Version of the 3mf file
|
||||||
unsigned int m_version;
|
unsigned int m_version;
|
||||||
bool m_check_version;
|
bool m_check_version;
|
||||||
@ -497,6 +499,7 @@ namespace Slic3r {
|
|||||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||||
IdToSlaSupportPointsMap m_sla_support_points;
|
IdToSlaSupportPointsMap m_sla_support_points;
|
||||||
IdToSlaDrainHolesMap m_sla_drain_holes;
|
IdToSlaDrainHolesMap m_sla_drain_holes;
|
||||||
|
PathToEmbossShapeFileMap m_path_to_emboss_shape_files;
|
||||||
std::string m_curr_metadata_name;
|
std::string m_curr_metadata_name;
|
||||||
std::string m_curr_characters;
|
std::string m_curr_characters;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
@ -523,6 +526,7 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
|
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
|
||||||
|
bool _is_svg_shape_file(const std::string &filename);
|
||||||
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||||
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
@ -534,6 +538,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
|
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
|
||||||
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
|
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
|
||||||
|
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
|
// handlers to parse the .model file
|
||||||
void _handle_start_model_xml_element(const char* name, const char** attributes);
|
void _handle_start_model_xml_element(const char* name, const char** attributes);
|
||||||
@ -762,6 +767,9 @@ namespace Slic3r {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (_is_svg_shape_file(name)) {
|
||||||
|
_extract_embossed_svg_shape_file(name, archive, stat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -936,6 +944,10 @@ namespace Slic3r {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _3MF_Importer::_is_svg_shape_file(const std::string &name) {
|
||||||
|
return name._Starts_with(MODEL_FOLDER) && boost::algorithm::ends_with(name, ".svg");
|
||||||
|
}
|
||||||
|
|
||||||
bool _3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
bool _3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||||
{
|
{
|
||||||
if (stat.m_uncomp_size == 0) {
|
if (stat.m_uncomp_size == 0) {
|
||||||
@ -1368,6 +1380,36 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _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());
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> file{new char[stat.m_uncomp_size + 1]};
|
||||||
|
if (file == nullptr){
|
||||||
|
add_error("Cannot alocate space for SVG file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void *) file.get(), (size_t) stat.m_uncomp_size, 0);
|
||||||
|
if (res == 0) {
|
||||||
|
add_error("Error while reading svg shape for emboss");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.get()[stat.m_uncomp_size] = '\0'; // Must be null terminated.
|
||||||
|
|
||||||
|
// 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 (ModelObject* object : m_model->objects)
|
||||||
|
for (ModelVolume *volume : object->volumes) {
|
||||||
|
std::optional<EmbossShape> &es = volume->emboss_shape;
|
||||||
|
if (!es.has_value())
|
||||||
|
continue;
|
||||||
|
if (filename.compare(es->svg_file.path_in_3mf) == 0)
|
||||||
|
es->svg_file.file_data = m_path_to_emboss_shape_files[filename];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
|
bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
|
||||||
{
|
{
|
||||||
if (stat.m_uncomp_size == 0) {
|
if (stat.m_uncomp_size == 0) {
|
||||||
@ -1977,7 +2019,7 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Definition of read/write method for EmbossShape
|
// Definition of read/write method for EmbossShape
|
||||||
static void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume& volume);
|
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);
|
static std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes);
|
||||||
|
|
||||||
bool _3MF_Importer::_handle_start_shape_configuration(const char **attributes, unsigned int num_attributes)
|
bool _3MF_Importer::_handle_start_shape_configuration(const char **attributes, unsigned int num_attributes)
|
||||||
@ -1994,7 +2036,21 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
ObjectMetadata::VolumeMetadata &volume = volumes.back();
|
ObjectMetadata::VolumeMetadata &volume = volumes.back();
|
||||||
volume.shape_configuration = read_emboss_shape(attributes, num_attributes);
|
volume.shape_configuration = read_emboss_shape(attributes, num_attributes);
|
||||||
return volume.shape_configuration.has_value();
|
if (!volume.shape_configuration.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Fill svg file content into shape_configuration
|
||||||
|
EmbossShape::SvgFile &svg = volume.shape_configuration->svg_file;
|
||||||
|
const std::string &path = svg.path_in_3mf;
|
||||||
|
if (path.empty()) // do not contain svg file
|
||||||
|
return true;
|
||||||
|
|
||||||
|
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 _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
|
bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
|
||||||
@ -2413,6 +2469,7 @@ namespace Slic3r {
|
|||||||
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64);
|
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64);
|
||||||
static void add_transformation(std::stringstream &stream, const Transform3d &tr);
|
static void add_transformation(std::stringstream &stream, const Transform3d &tr);
|
||||||
private:
|
private:
|
||||||
|
void _publish(Model &model);
|
||||||
bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data);
|
bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data);
|
||||||
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
|
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
|
||||||
bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data);
|
bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data);
|
||||||
@ -3359,7 +3416,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
if (const std::optional<EmbossShape> &es = volume->emboss_shape;
|
if (const std::optional<EmbossShape> &es = volume->emboss_shape;
|
||||||
es.has_value())
|
es.has_value())
|
||||||
to_xml(stream, *es, *volume);
|
to_xml(stream, *es, *volume, archive);
|
||||||
|
|
||||||
if (const std::optional<TextConfiguration> &tc = volume->text_configuration;
|
if (const std::optional<TextConfiguration> &tc = volume->text_configuration;
|
||||||
tc.has_value())
|
tc.has_value())
|
||||||
@ -3681,21 +3738,37 @@ Transform3d create_fix(const std::optional<Transform3d> &prev, const ModelVolume
|
|||||||
return *prev * fix_trmat;
|
return *prev * fix_trmat;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string(const ExPolygonsWithIds &shapes)
|
bool to_xml(std::stringstream &stream, const EmbossShape::SvgFile &svg, const ModelVolume &volume, mz_zip_archive &archive){
|
||||||
{
|
assert(!svg.path_in_3mf.empty());
|
||||||
// TODO: Need to implement
|
if (svg.path_in_3mf.empty())
|
||||||
return {};
|
return false; // 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) << "\" ";
|
||||||
|
|
||||||
|
char *data = svg.file_data.get();
|
||||||
|
assert(data != nullptr);
|
||||||
|
if (data == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// NOTE: file data must be null terminated
|
||||||
|
size_t size = 0;
|
||||||
|
for (char *c = data; *c != '\0'; ++c) ++size;
|
||||||
|
if (!mz_zip_writer_add_mem(&archive, svg.path_in_3mf.c_str(), (const void *) data, size, MZ_DEFAULT_COMPRESSION))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume &volume) {
|
void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume &volume, mz_zip_archive &archive)
|
||||||
|
{
|
||||||
stream << " <" << SHAPE_TAG << " ";
|
stream << " <" << SHAPE_TAG << " ";
|
||||||
stream << SVG_FILE_PATH_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(es.svg_file.path) << "\" ";
|
if(!to_xml(stream, es.svg_file, volume, archive))
|
||||||
stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" ";
|
BOOST_LOG_TRIVIAL(warning) << "Can't write svg file defiden embossed shape into 3mf";
|
||||||
|
|
||||||
std::string expolygons_str = to_string(es.shapes_with_ids); // cereal serialize expolygons
|
stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" ";
|
||||||
stream << SHAPE_EXPOLYS_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(expolygons_str) << "\" ";
|
|
||||||
|
|
||||||
// projection
|
// projection
|
||||||
const EmbossProjection &p = es.projection;
|
const EmbossProjection &p = es.projection;
|
||||||
@ -3708,11 +3781,11 @@ void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume
|
|||||||
stream << TRANSFORM_ATTR << "=\"";
|
stream << TRANSFORM_ATTR << "=\"";
|
||||||
_3MF_Exporter::add_transformation(stream, fix);
|
_3MF_Exporter::add_transformation(stream, fix);
|
||||||
stream << "\" ";
|
stream << "\" ";
|
||||||
|
|
||||||
stream << "/>\n"; // end SHAPE_TAG
|
stream << "/>\n"; // end SHAPE_TAG
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes) {
|
std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes) {
|
||||||
|
|
||||||
double scale = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
|
double scale = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
|
||||||
|
|
||||||
EmbossProjection projection;
|
EmbossProjection projection;
|
||||||
@ -3726,9 +3799,13 @@ std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned i
|
|||||||
if (!fix_tr_mat_str.empty()) {
|
if (!fix_tr_mat_str.empty()) {
|
||||||
fix_tr_mat = get_transform_from_3mf_specs_string(fix_tr_mat_str);
|
fix_tr_mat = get_transform_from_3mf_specs_string(fix_tr_mat_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string file_path = get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_ATTR);
|
std::string file_path = get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_ATTR);
|
||||||
|
std::string file_path_3mf = get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_IN_3MF_ATTR);
|
||||||
ExPolygonsWithIds shapes; // TODO: need to implement
|
ExPolygonsWithIds shapes; // TODO: need to implement
|
||||||
return EmbossShape{shapes, scale, std::move(projection), std::move(fix_tr_mat), std::move(file_path)};
|
|
||||||
|
EmbossShape::SvgFile svg{file_path, file_path_3mf};
|
||||||
|
return EmbossShape{shapes, scale, std::move(projection), std::move(fix_tr_mat), std::move(svg)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,13 +54,43 @@ NSVGimage_ptr nsvgParseFromFile(const std::string &filename, const char *units,
|
|||||||
return {image, ::nsvgDelete};
|
return {image, ::nsvgDelete};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool save(const NSVGimage &image, const std::string &svg_file_path)
|
std::unique_ptr<char[]> read_from_disk(const std::string& path)
|
||||||
{
|
{
|
||||||
FILE * file = boost::nowide::fopen(svg_file_path.c_str(), "w");
|
FILE *fp = boost::nowide::fopen(path.c_str(), "rb");
|
||||||
if (file == NULL)
|
if (!fp)
|
||||||
return false;
|
return nullptr;
|
||||||
|
|
||||||
fprintf(file, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
|
fseek(fp, 0, SEEK_END);
|
||||||
|
size_t size = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> result{new char[size + 1]};
|
||||||
|
if (result == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (fread(result.get(), 1, size, fp) != size)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
result.get()[size] = '\0'; // Must be null terminated.
|
||||||
|
fclose(fp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSVGimage_ptr nsvgParse(const std::shared_ptr<char[]> file_data, const char *units, float dpi){
|
||||||
|
size_t size = 0;
|
||||||
|
for (char *c = file_data.get(); *c != '\0'; ++c)
|
||||||
|
++size;
|
||||||
|
|
||||||
|
// NOTE: nsvg parser consume data from pointer
|
||||||
|
std::unique_ptr<char[]> data_copy(new char[size]);
|
||||||
|
memcpy(data_copy.get(), file_data.get(), size);
|
||||||
|
NSVGimage *image = ::nsvgParse(data_copy.get(), units, dpi);
|
||||||
|
return {image, ::nsvgDelete};
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(const NSVGimage &image, std::ostream &data)
|
||||||
|
{
|
||||||
|
data << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
|
||||||
|
|
||||||
// tl .. top left
|
// tl .. top left
|
||||||
Vec2f tl(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
|
Vec2f tl(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
|
||||||
@ -76,9 +106,11 @@ bool save(const NSVGimage &image, const std::string &svg_file_path)
|
|||||||
Vec2f s = br - tl;
|
Vec2f s = br - tl;
|
||||||
Point size = s.cast<Point::coord_type>();
|
Point size = s.cast<Point::coord_type>();
|
||||||
|
|
||||||
fprintf(file, "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"%dmm\" height=\"%dmm\" viewBox=\"0 0 %d %d\" >\n",
|
data << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
||||||
size.x(), size.y(), size.x(), size.y());
|
<< "width=\"" << size.x() << "mm\" "
|
||||||
fprintf(file, "<!-- Created with PrusaSlicer (https://www.prusa3d.com/prusaslicer/) -->\n");
|
<< "height=\"" << size.y() << "mm\" "
|
||||||
|
<< "viewBox=\"0 0 " << size.x() << " " << size.y() << "\" >\n";
|
||||||
|
data << "<!-- Created with PrusaSlicer (https://www.prusa3d.com/prusaslicer/) -->\n";
|
||||||
|
|
||||||
std::array<char, 128> buffer;
|
std::array<char, 128> buffer;
|
||||||
auto write_point = [&tl, &buffer](std::string &d, const float *p) {
|
auto write_point = [&tl, &buffer](std::string &d, const float *p) {
|
||||||
@ -143,10 +175,18 @@ bool save(const NSVGimage &image, const std::string &svg_file_path)
|
|||||||
type = Type::close;
|
type = Type::close;
|
||||||
d += "Z"; // closed path
|
d += "Z"; // closed path
|
||||||
}
|
}
|
||||||
fprintf(file, "<path fill=\"#D2D2D2\" d=\"%s\" />\n", d.c_str());
|
data << "<path fill=\"#D2D2D2\" d=\"" << d << "\" />\n";
|
||||||
}
|
}
|
||||||
fprintf(file, "</svg>\n");
|
data << "</svg>\n";
|
||||||
fclose(file);
|
}
|
||||||
|
|
||||||
|
bool save(const NSVGimage &image, const std::string &svg_file_path)
|
||||||
|
{
|
||||||
|
std::ofstream file{svg_file_path};
|
||||||
|
if (!file.is_open())
|
||||||
|
return false;
|
||||||
|
save(image, file);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
#include "Polygon.hpp"
|
#include "Polygon.hpp"
|
||||||
#include "ExPolygon.hpp"
|
#include "ExPolygon.hpp"
|
||||||
#include "nanosvg/nanosvg.h" // load SVG file
|
#include "nanosvg/nanosvg.h" // load SVG file
|
||||||
@ -26,8 +27,14 @@ ExPolygons to_expolygons(const NSVGimage &image, float tessTol = 10., int max_le
|
|||||||
|
|
||||||
void bounds(const NSVGimage &image, Vec2f &min, Vec2f &max);
|
void bounds(const NSVGimage &image, Vec2f &min, Vec2f &max);
|
||||||
|
|
||||||
|
// read text data from file
|
||||||
|
std::unique_ptr<char[]> read_from_disk(const std::string &path);
|
||||||
|
|
||||||
using NSVGimage_ptr = std::unique_ptr<NSVGimage, void (*)(NSVGimage*)>;
|
using NSVGimage_ptr = std::unique_ptr<NSVGimage, void (*)(NSVGimage*)>;
|
||||||
NSVGimage_ptr nsvgParseFromFile(const std::string &svg_file_path, const char *units = "mm", float dpi = 96.0f);
|
NSVGimage_ptr nsvgParseFromFile(const std::string &svg_file_path, const char *units = "mm", float dpi = 96.0f);
|
||||||
|
NSVGimage_ptr nsvgParse(const std::shared_ptr<char[]> file_data, const char *units = "mm", float dpi = 96.0f);
|
||||||
|
|
||||||
|
void save(const NSVGimage &image, std::ostream &data);
|
||||||
bool save(const NSVGimage &image, const std::string &svg_file_path);
|
bool save(const NSVGimage &image, const std::string &svg_file_path);
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
#endif // slic3r_NSVGUtils_hpp_
|
#endif // slic3r_NSVGUtils_hpp_
|
||||||
|
@ -1229,7 +1229,14 @@ bool GLGizmoSVG::draw_preview(){
|
|||||||
if (dlg.ShowModal() == wxID_OK ){
|
if (dlg.ShowModal() == wxID_OK ){
|
||||||
wxString out_path = dlg.GetPath();
|
wxString out_path = dlg.GetPath();
|
||||||
std::string path{out_path.c_str()};
|
std::string path{out_path.c_str()};
|
||||||
Slic3r::save(*m_volume_shape.svg_file.image, path);
|
//Slic3r::save(*m_volume_shape.svg_file.image, path);
|
||||||
|
|
||||||
|
std::ofstream stream(path);
|
||||||
|
if (stream.is_open()){
|
||||||
|
stream << svg.file_data.get();
|
||||||
|
} else {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Opening file: \"" << path << "\" Failed";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (ImGui::IsItemHovered()) {
|
} else if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("%s", _u8L("Save as '.svg' file").c_str());
|
ImGui::SetTooltip("%s", _u8L("Save as '.svg' file").c_str());
|
||||||
|
@ -6666,6 +6666,103 @@ void Plater::export_amf()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string get_file_name(const std::string &file_path)
|
||||||
|
{
|
||||||
|
size_t pos_last_delimiter = file_path.find_last_of("/\\");
|
||||||
|
size_t pos_point = file_path.find_last_of('.');
|
||||||
|
size_t offset = pos_last_delimiter + 1;
|
||||||
|
size_t count = pos_point - pos_last_delimiter - 1;
|
||||||
|
return file_path.substr(offset, count);
|
||||||
|
}
|
||||||
|
using SvgFile = EmbossShape::SvgFile;
|
||||||
|
using SvgFiles = std::vector<SvgFile*>;
|
||||||
|
std::string create_unique_3mf_filepath(const std::string &file, const SvgFiles svgs)
|
||||||
|
{
|
||||||
|
// const std::string MODEL_FOLDER = "3D/"; // copy from file 3mf.cpp
|
||||||
|
std::string path_in_3mf = "3D/" + file + ".svg";
|
||||||
|
size_t suffix_number = 0;
|
||||||
|
bool is_unique = false;
|
||||||
|
do{
|
||||||
|
is_unique = true;
|
||||||
|
path_in_3mf = "3D/" + file + ((suffix_number++)? ("_" + std::to_string(suffix_number)) : "") + ".svg";
|
||||||
|
for (SvgFile *svgfile : svgs) {
|
||||||
|
if (svgfile->path_in_3mf.empty())
|
||||||
|
continue;
|
||||||
|
if (svgfile->path_in_3mf.compare(path_in_3mf) == 0) {
|
||||||
|
is_unique = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!is_unique);
|
||||||
|
return path_in_3mf;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_by_local_path(SvgFile &svg, const SvgFiles& svgs)
|
||||||
|
{
|
||||||
|
// Try to find already used svg file
|
||||||
|
for (SvgFile *svg_ : svgs) {
|
||||||
|
if (svg_->path_in_3mf.empty())
|
||||||
|
continue;
|
||||||
|
if (svg.path.compare(svg_->path) == 0) {
|
||||||
|
svg.path_in_3mf = svg_->path_in_3mf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Function to secure private data before store to 3mf
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">Data(also private) to clean before publishing</param>
|
||||||
|
void publish(Model &model) {
|
||||||
|
|
||||||
|
// SVG file publishing
|
||||||
|
bool exist_new = false;
|
||||||
|
SvgFiles svgfiles;
|
||||||
|
for (ModelObject *object: model.objects){
|
||||||
|
for (ModelVolume *volume : object->volumes) {
|
||||||
|
if (!volume->emboss_shape.has_value())
|
||||||
|
continue;
|
||||||
|
SvgFile* svg = &volume->emboss_shape->svg_file;
|
||||||
|
|
||||||
|
if (svg->path_in_3mf.empty())
|
||||||
|
exist_new = true;
|
||||||
|
svgfiles.push_back(svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exist_new){
|
||||||
|
MessageDialog dialog(nullptr,
|
||||||
|
_L("Are you sure you want to store original SVGs with their local path into .3mf ?\n "
|
||||||
|
"When you hit 'NO', all Embossed shape will not be editable any more."),
|
||||||
|
_L("Private protection"), wxYES_NO | wxICON_QUESTION);
|
||||||
|
if (dialog.ShowModal() == wxID_NO){
|
||||||
|
for (ModelObject *object : model.objects)
|
||||||
|
for (ModelVolume *volume : object->volumes)
|
||||||
|
if (volume->emboss_shape.has_value())
|
||||||
|
volume->emboss_shape.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SvgFile* svgfile : svgfiles){
|
||||||
|
if (!svgfile->path_in_3mf.empty())
|
||||||
|
continue; // already suggested path (previous save)
|
||||||
|
|
||||||
|
// create unique name for svgs, when local path differ
|
||||||
|
std::string filename = "unknown";
|
||||||
|
if (!svgfile->path.empty()) {
|
||||||
|
if (set_by_local_path(*svgfile, svgfiles))
|
||||||
|
continue;
|
||||||
|
// check whether original filename is already in:
|
||||||
|
filename = get_file_name(svgfile->path);
|
||||||
|
}
|
||||||
|
svgfile->path_in_3mf = create_unique_3mf_filepath(filename, svgfiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Plater::export_3mf(const boost::filesystem::path& output_path)
|
bool Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty()) {
|
if (p->model.objects.empty()) {
|
||||||
@ -6686,6 +6783,10 @@ bool Plater::export_3mf(const boost::filesystem::path& output_path)
|
|||||||
if (!path.Lower().EndsWith(".3mf"))
|
if (!path.Lower().EndsWith(".3mf"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// take care about private data stored into .3mf
|
||||||
|
// modify model
|
||||||
|
publish(p->model);
|
||||||
|
|
||||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||||
const std::string path_u8 = into_u8(path);
|
const std::string path_u8 = into_u8(path);
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user