mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-02 17:08:42 +03:00
PRUSA 2.7.0
This commit is contained in:
@@ -36,6 +36,10 @@ namespace pt = boost::property_tree;
|
||||
#include "miniz_extension.hpp"
|
||||
|
||||
#include "TextConfiguration.hpp"
|
||||
#include "EmbossShape.hpp"
|
||||
#include "ExPolygonSerialize.hpp"
|
||||
|
||||
#include "NSVGUtils.hpp"
|
||||
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
@@ -157,12 +161,11 @@ static constexpr const char *FONT_DESCRIPTOR_TYPE_ATTR = "font_descriptor_type";
|
||||
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 *DEPTH_ATTR = "depth";
|
||||
static constexpr const char *USE_SURFACE_ATTR = "use_surface";
|
||||
static constexpr const char *BOLDNESS_ATTR = "boldness";
|
||||
static constexpr const char *SKEW_ATTR = "skew";
|
||||
static constexpr const char *DISTANCE_ATTR = "distance";
|
||||
static constexpr const char *ANGLE_ATTR = "angle";
|
||||
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";
|
||||
@@ -170,6 +173,15 @@ 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";
|
||||
|
||||
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";
|
||||
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
|
||||
const char* VALID_OBJECT_TYPES[] =
|
||||
{
|
||||
@@ -416,6 +428,7 @@ namespace Slic3r {
|
||||
MetadataList metadata;
|
||||
RepairedMeshErrors mesh_stats;
|
||||
std::optional<TextConfiguration> text_configuration;
|
||||
std::optional<EmbossShape> shape_configuration;
|
||||
VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id)
|
||||
: first_triangle_id(first_triangle_id)
|
||||
, last_triangle_id(last_triangle_id)
|
||||
@@ -454,6 +467,7 @@ namespace Slic3r {
|
||||
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>>;
|
||||
// Version of the 3mf file
|
||||
unsigned int m_version;
|
||||
bool m_check_version;
|
||||
@@ -483,6 +497,7 @@ namespace Slic3r {
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
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;
|
||||
@@ -509,6 +524,7 @@ namespace Slic3r {
|
||||
}
|
||||
|
||||
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) const;
|
||||
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_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
@@ -520,6 +536,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);
|
||||
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
|
||||
void _handle_start_model_xml_element(const char* name, const char** attributes);
|
||||
@@ -570,6 +587,7 @@ namespace Slic3r {
|
||||
bool _handle_end_metadata();
|
||||
|
||||
bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes);
|
||||
bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes);
|
||||
|
||||
bool _create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
|
||||
|
||||
@@ -747,6 +765,9 @@ namespace Slic3r {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (_is_svg_shape_file(name)) {
|
||||
_extract_embossed_svg_shape_file(name, archive, stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,6 +942,9 @@ namespace Slic3r {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_is_svg_shape_file(const std::string &name) const {
|
||||
return boost::starts_with(name, MODEL_FOLDER) && boost::ends_with(name, ".svg");
|
||||
}
|
||||
bool _3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||
{
|
||||
if (stat.m_uncomp_size == 0) {
|
||||
@@ -1353,6 +1377,31 @@ 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());
|
||||
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];
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@@ -1552,6 +1601,8 @@ namespace Slic3r {
|
||||
res = _handle_start_config_volume_mesh(attributes, num_attributes);
|
||||
else if (::strcmp(METADATA_TAG, name) == 0)
|
||||
res = _handle_start_config_metadata(attributes, num_attributes);
|
||||
else if (::strcmp(SHAPE_TAG, name) == 0)
|
||||
res = _handle_start_shape_configuration(attributes, num_attributes);
|
||||
else if (::strcmp(TEXT_TAG, name) == 0)
|
||||
res = _handle_start_text_configuration(attributes, num_attributes);
|
||||
|
||||
@@ -1905,7 +1956,14 @@ namespace Slic3r {
|
||||
public:
|
||||
TextConfigurationSerialization() = delete;
|
||||
|
||||
static const boost::bimap<EmbossStyle::Type, std::string_view> type_to_name;
|
||||
using TypeToName = boost::bimap<EmbossStyle::Type, std::string_view>;
|
||||
static const TypeToName type_to_name;
|
||||
|
||||
using HorizontalAlignToName = boost::bimap<FontProp::HorizontalAlign, std::string_view>;
|
||||
static const HorizontalAlignToName horizontal_align_to_name;
|
||||
|
||||
using VerticalAlignToName = boost::bimap<FontProp::VerticalAlign, std::string_view>;
|
||||
static const VerticalAlignToName vertical_align_to_name;
|
||||
|
||||
static EmbossStyle::Type get_type(std::string_view type) {
|
||||
const auto& to_type = TextConfigurationSerialization::type_to_name.right;
|
||||
@@ -1924,8 +1982,8 @@ namespace Slic3r {
|
||||
}
|
||||
|
||||
static void to_xml(std::stringstream &stream, const TextConfiguration &tc);
|
||||
static void create_fix_and_store(std::stringstream &stream, TextConfiguration tc, const ModelVolume& volume);
|
||||
static std::optional<TextConfiguration> read(const char **attributes, unsigned int num_attributes);
|
||||
static EmbossShape read_old(const char **attributes, unsigned int num_attributes);
|
||||
};
|
||||
|
||||
bool _3MF_Importer::_handle_start_text_configuration(const char **attributes, unsigned int num_attributes)
|
||||
@@ -1941,7 +1999,54 @@ namespace Slic3r {
|
||||
}
|
||||
ObjectMetadata::VolumeMetadata& volume = object->second.volumes.back();
|
||||
volume.text_configuration = TextConfigurationSerialization::read(attributes, num_attributes);
|
||||
return volume.text_configuration.has_value();
|
||||
if (!volume.text_configuration.has_value())
|
||||
return false;
|
||||
|
||||
// Is 3mf version with shapes?
|
||||
if (volume.shape_configuration.has_value())
|
||||
return true;
|
||||
|
||||
// Back compatibility for 3mf version without shapes
|
||||
volume.shape_configuration = TextConfigurationSerialization::read_old(attributes, num_attributes);
|
||||
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 _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 _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
|
||||
@@ -2224,8 +2329,9 @@ namespace Slic3r {
|
||||
volume->supported_facets.shrink_to_fit();
|
||||
volume->seam_facets.shrink_to_fit();
|
||||
volume->mmu_segmentation_facets.shrink_to_fit();
|
||||
auto &tc = volume_data.text_configuration;
|
||||
if (tc.has_value()) {
|
||||
if (auto &es = volume_data.shape_configuration; es.has_value())
|
||||
volume->emboss_shape = std::move(es);
|
||||
if (auto &tc = volume_data.text_configuration; tc.has_value())
|
||||
volume->text_configuration = std::move(tc);
|
||||
|
||||
//// Transformation before store to 3mf
|
||||
@@ -2239,7 +2345,6 @@ namespace Slic3r {
|
||||
//volume->text_configuration->fix_3mf_tr =
|
||||
// pre_trmat.inverse() *
|
||||
// volume->get_transformation().get_matrix();
|
||||
}
|
||||
|
||||
// apply the remaining volume's metadata
|
||||
for (const Metadata& metadata : volume_data.metadata) {
|
||||
@@ -2371,6 +2476,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);
|
||||
static void add_transformation(std::stringstream &stream, const Transform3d &tr);
|
||||
private:
|
||||
void _publish(Model &model);
|
||||
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_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data);
|
||||
@@ -3315,10 +3421,13 @@ namespace Slic3r {
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << 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);
|
||||
// stores volume's text data
|
||||
const auto &tc = volume->text_configuration;
|
||||
if (tc.has_value())
|
||||
TextConfigurationSerialization::create_fix_and_store(stream, *tc, *volume);
|
||||
if (const std::optional<TextConfiguration> &tc = volume->text_configuration;
|
||||
tc.has_value())
|
||||
TextConfigurationSerialization::to_xml(stream, *tc);
|
||||
|
||||
// stores mesh's statistics
|
||||
const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
|
||||
@@ -3489,27 +3598,71 @@ bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config,
|
||||
return res;
|
||||
}
|
||||
|
||||
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
|
||||
/// <summary>
|
||||
/// TextConfiguration serialization
|
||||
/// </summary>
|
||||
using TypeToName = boost::bimap<EmbossStyle::Type, std::string_view>;
|
||||
const TypeToName TextConfigurationSerialization::type_to_name =
|
||||
const TextConfigurationSerialization::TypeToName TextConfigurationSerialization::type_to_name =
|
||||
boost::assign::list_of<TypeToName::relation>
|
||||
(EmbossStyle::Type::file_path, "file_name")
|
||||
(EmbossStyle::Type::wx_win_font_descr, "wxFontDescriptor_Windows")
|
||||
(EmbossStyle::Type::wx_lin_font_descr, "wxFontDescriptor_Linux")
|
||||
(EmbossStyle::Type::wx_mac_font_descr, "wxFontDescriptor_MacOsX");
|
||||
|
||||
const TextConfigurationSerialization::HorizontalAlignToName TextConfigurationSerialization::horizontal_align_to_name =
|
||||
boost::assign::list_of<HorizontalAlignToName::relation>
|
||||
(FontProp::HorizontalAlign::left, "left")
|
||||
(FontProp::HorizontalAlign::center, "center")
|
||||
(FontProp::HorizontalAlign::right, "right");
|
||||
|
||||
const TextConfigurationSerialization::VerticalAlignToName TextConfigurationSerialization::vertical_align_to_name =
|
||||
boost::assign::list_of<VerticalAlignToName::relation>
|
||||
(FontProp::VerticalAlign::top, "top")
|
||||
(FontProp::VerticalAlign::center, "middle")
|
||||
(FontProp::VerticalAlign::bottom, "bottom");
|
||||
void TextConfigurationSerialization::to_xml(std::stringstream &stream, const TextConfiguration &tc)
|
||||
{
|
||||
stream << " <" << TEXT_TAG << " ";
|
||||
|
||||
stream << TEXT_DATA_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(tc.text) << "\" ";
|
||||
// font item
|
||||
const EmbossStyle &fi = tc.style;
|
||||
stream << STYLE_NAME_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(fi.name) << "\" ";
|
||||
stream << FONT_DESCRIPTOR_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(fi.path) << "\" ";
|
||||
stream << FONT_DESCRIPTOR_TYPE_ATTR << "=\"" << TextConfigurationSerialization::get_name(fi.type) << "\" ";
|
||||
const EmbossStyle &style = tc.style;
|
||||
stream << STYLE_NAME_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(style.name) << "\" ";
|
||||
stream << FONT_DESCRIPTOR_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(style.path) << "\" ";
|
||||
constexpr std::string_view dafault_type{"undefined"};
|
||||
std::string_view style_type = bimap_cvt(type_to_name, style.type, dafault_type);
|
||||
stream << FONT_DESCRIPTOR_TYPE_ATTR << "=\"" << style_type << "\" ";
|
||||
|
||||
// font property
|
||||
const FontProp &fp = tc.style.prop;
|
||||
@@ -3519,17 +3672,14 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex
|
||||
stream << LINE_GAP_ATTR << "=\"" << *fp.line_gap << "\" ";
|
||||
|
||||
stream << LINE_HEIGHT_ATTR << "=\"" << fp.size_in_mm << "\" ";
|
||||
stream << DEPTH_ATTR << "=\"" << fp.emboss << "\" ";
|
||||
if (fp.use_surface)
|
||||
stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" ";
|
||||
if (fp.boldness.has_value())
|
||||
stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" ";
|
||||
if (fp.skew.has_value())
|
||||
stream << SKEW_ATTR << "=\"" << *fp.skew << "\" ";
|
||||
if (fp.distance.has_value())
|
||||
stream << DISTANCE_ATTR << "=\"" << *fp.distance << "\" ";
|
||||
if (fp.angle.has_value())
|
||||
stream << ANGLE_ATTR << "=\"" << *fp.angle << "\" ";
|
||||
if (fp.per_glyph)
|
||||
stream << PER_GLYPH_ATTR << "=\"" << 1 << "\" ";
|
||||
stream << HORIZONTAL_ALIGN_ATTR << "=\"" << bimap_cvt(horizontal_align_to_name, fp.align.first, dafault_type) << "\" ";
|
||||
stream << VERTICAL_ALIGN_ATTR << "=\"" << bimap_cvt(vertical_align_to_name, fp.align.second, dafault_type) << "\" ";
|
||||
if (fp.collection_number.has_value())
|
||||
stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" ";
|
||||
// font descriptor
|
||||
@@ -3542,50 +3692,44 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex
|
||||
if (fp.weight.has_value())
|
||||
stream << FONT_WEIGHT_ATTR << "=\"" << *fp.weight << "\" ";
|
||||
|
||||
// FIX of baked transformation
|
||||
assert(tc.fix_3mf_tr.has_value());
|
||||
stream << TRANSFORM_ATTR << "=\"";
|
||||
_3MF_Exporter::add_transformation(stream, *tc.fix_3mf_tr);
|
||||
stream << "\" ";
|
||||
|
||||
stream << "/>\n"; // end TEXT_TAG
|
||||
}
|
||||
namespace {
|
||||
|
||||
void TextConfigurationSerialization::create_fix_and_store(
|
||||
std::stringstream &stream, TextConfiguration tc, const ModelVolume &volume)
|
||||
{
|
||||
const auto& vertices = volume.mesh().its.vertices;
|
||||
assert(!vertices.empty());
|
||||
if (vertices.empty()) {
|
||||
to_xml(stream, tc);
|
||||
return;
|
||||
FontProp::HorizontalAlign read_horizontal_align(const char **attributes, unsigned int num_attributes, const TextConfigurationSerialization::HorizontalAlignToName& horizontal_align_to_name){
|
||||
std::string horizontal_align_str = get_attribute_value_string(attributes, num_attributes, HORIZONTAL_ALIGN_ATTR);
|
||||
// FIX of baked transformation
|
||||
if (horizontal_align_str.empty())
|
||||
return FontProp::HorizontalAlign::center;
|
||||
|
||||
if (horizontal_align_str.length() == 1) {
|
||||
int horizontal_align_int = 0;
|
||||
if(boost::spirit::qi::parse(horizontal_align_str.c_str(), horizontal_align_str.c_str() + 1, boost::spirit::qi::int_, horizontal_align_int))
|
||||
return static_cast<FontProp::HorizontalAlign>(horizontal_align_int);
|
||||
}
|
||||
|
||||
return bimap_cvt(horizontal_align_to_name, std::string_view(horizontal_align_str), FontProp::HorizontalAlign::center);
|
||||
}
|
||||
|
||||
// IMPROVE: check if volume was modified (translated, rotated OR scaled)
|
||||
FontProp::VerticalAlign read_vertical_align(const char **attributes, unsigned int num_attributes, const TextConfigurationSerialization::VerticalAlignToName& vertical_align_to_name){
|
||||
std::string vertical_align_str = get_attribute_value_string(attributes, num_attributes, VERTICAL_ALIGN_ATTR);
|
||||
// when no change do not calculate transformation only store original fix matrix
|
||||
|
||||
// Create transformation used after load actual stored volume
|
||||
const Transform3d &actual_trmat = volume.get_transformation().get_matrix();
|
||||
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);
|
||||
if (vertical_align_str.empty())
|
||||
return FontProp::VerticalAlign::center;
|
||||
|
||||
Transform3d fix_trmat = actual_trmat.inverse() * post_trmat;
|
||||
if (!tc.fix_3mf_tr.has_value()) {
|
||||
tc.fix_3mf_tr = fix_trmat;
|
||||
} else if (!fix_trmat.isApprox(Transform3d::Identity(), 1e-5)) {
|
||||
tc.fix_3mf_tr = *tc.fix_3mf_tr * fix_trmat;
|
||||
// Back compatibility
|
||||
// PS 2.6.1 store indices(0|1|2) instead of text for align
|
||||
if (vertical_align_str.length() == 1) {
|
||||
int vertical_align_int = 0;
|
||||
if(boost::spirit::qi::parse(vertical_align_str.c_str(), vertical_align_str.c_str() + 1, boost::spirit::qi::int_, vertical_align_int))
|
||||
return static_cast<FontProp::VerticalAlign>(vertical_align_int);
|
||||
}
|
||||
|
||||
return bimap_cvt(vertical_align_to_name, std::string_view(vertical_align_str), FontProp::VerticalAlign::center);
|
||||
}
|
||||
to_xml(stream, tc);
|
||||
}
|
||||
|
||||
std::optional<TextConfiguration> TextConfigurationSerialization::read(const char **attributes, unsigned int num_attributes)
|
||||
@@ -3601,19 +3745,16 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
|
||||
float skew = get_attribute_value_float(attributes, num_attributes, SKEW_ATTR);
|
||||
if (std::fabs(skew) > std::numeric_limits<float>::epsilon())
|
||||
fp.skew = skew;
|
||||
float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR);
|
||||
if (std::fabs(distance) > std::numeric_limits<float>::epsilon())
|
||||
fp.distance = distance;
|
||||
int use_surface = get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
|
||||
if (use_surface == 1) fp.use_surface = true;
|
||||
float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR);
|
||||
if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
|
||||
fp.angle = angle;
|
||||
int per_glyph = get_attribute_value_int(attributes, num_attributes, PER_GLYPH_ATTR);
|
||||
if (per_glyph == 1) fp.per_glyph = true;
|
||||
|
||||
fp.align = FontProp::Align(
|
||||
read_horizontal_align(attributes, num_attributes, horizontal_align_to_name),
|
||||
read_vertical_align(attributes, num_attributes, vertical_align_to_name));
|
||||
int collection_number = get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR);
|
||||
if (collection_number > 0) fp.collection_number = static_cast<unsigned int>(collection_number);
|
||||
|
||||
fp.size_in_mm = get_attribute_value_float(attributes, num_attributes, LINE_HEIGHT_ATTR);
|
||||
fp.emboss = get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);
|
||||
|
||||
std::string family = get_attribute_value_string(attributes, num_attributes, FONT_FAMILY_ATTR);
|
||||
if (!family.empty()) fp.family = family;
|
||||
@@ -3627,10 +3768,133 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
|
||||
std::string style_name = get_attribute_value_string(attributes, num_attributes, STYLE_NAME_ATTR);
|
||||
std::string font_descriptor = get_attribute_value_string(attributes, num_attributes, FONT_DESCRIPTOR_ATTR);
|
||||
std::string type_str = get_attribute_value_string(attributes, num_attributes, FONT_DESCRIPTOR_TYPE_ATTR);
|
||||
EmbossStyle::Type type = TextConfigurationSerialization::get_type(type_str);
|
||||
EmbossStyle fi{ style_name, std::move(font_descriptor), type, std::move(fp) };
|
||||
EmbossStyle::Type type = bimap_cvt(type_to_name, std::string_view{type_str}, EmbossStyle::Type::undefined);
|
||||
|
||||
std::string text = get_attribute_value_string(attributes, num_attributes, TEXT_DATA_ATTR);
|
||||
EmbossStyle es{style_name, std::move(font_descriptor), type, std::move(fp)};
|
||||
return TextConfiguration{std::move(es), std::move(text)};
|
||||
}
|
||||
|
||||
EmbossShape TextConfigurationSerialization::read_old(const char **attributes, unsigned int num_attributes)
|
||||
{
|
||||
EmbossShape es;
|
||||
std::string fix_tr_mat_str = get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR);
|
||||
if (!fix_tr_mat_str.empty())
|
||||
es.fix_3mf_tr = get_transform_from_3mf_specs_string(fix_tr_mat_str);
|
||||
|
||||
|
||||
if (get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR) == 1)
|
||||
es.projection.use_surface = true;
|
||||
|
||||
es.projection.depth = get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);
|
||||
|
||||
int use_surface = get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
|
||||
if (use_surface == 1)
|
||||
es.projection.use_surface = true;
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
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
|
||||
const Transform3d &actual_trmat = volume.get_matrix();
|
||||
|
||||
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 << "=\"";
|
||||
_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 = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
|
||||
int unhealed = get_attribute_value_int(attributes, num_attributes, UNHEALED_ATTR);
|
||||
bool is_healed = unhealed != 1;
|
||||
|
||||
EmbossProjection projection;
|
||||
projection.depth = get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);
|
||||
if (is_approx(projection.depth, 0.))
|
||||
projection.depth = 10.;
|
||||
|
||||
int use_surface = 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 = get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR);
|
||||
@@ -3638,7 +3902,19 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
|
||||
fix_tr_mat = get_transform_from_3mf_specs_string(fix_tr_mat_str);
|
||||
}
|
||||
|
||||
return TextConfiguration{std::move(fi), std::move(text), std::move(fix_tr_mat)};
|
||||
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);
|
||||
|
||||
// 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)};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -104,21 +104,21 @@ typedef struct anycubicsla_format_header
|
||||
{
|
||||
char tag[12];
|
||||
std::uint32_t payload_size;
|
||||
std::float_t pixel_size_um;
|
||||
std::float_t layer_height_mm;
|
||||
std::float_t exposure_time_s;
|
||||
std::float_t delay_before_exposure_s;
|
||||
std::float_t bottom_exposure_time_s;
|
||||
std::float_t bottom_layer_count;
|
||||
std::float_t lift_distance_mm;
|
||||
std::float_t lift_speed_mms;
|
||||
std::float_t retract_speed_mms;
|
||||
std::float_t volume_ml;
|
||||
float pixel_size_um;
|
||||
float layer_height_mm;
|
||||
float exposure_time_s;
|
||||
float delay_before_exposure_s;
|
||||
float bottom_exposure_time_s;
|
||||
float bottom_layer_count;
|
||||
float lift_distance_mm;
|
||||
float lift_speed_mms;
|
||||
float retract_speed_mms;
|
||||
float volume_ml;
|
||||
std::uint32_t antialiasing;
|
||||
std::uint32_t res_x;
|
||||
std::uint32_t res_y;
|
||||
std::float_t weight_g;
|
||||
std::float_t price;
|
||||
float weight_g;
|
||||
float price;
|
||||
std::uint32_t price_currency;
|
||||
std::uint32_t per_layer_override; // ? unknown meaning ?
|
||||
std::uint32_t print_time_s;
|
||||
@@ -149,19 +149,19 @@ typedef struct anycubicsla_format_layer
|
||||
{
|
||||
std::uint32_t image_offset;
|
||||
std::uint32_t image_size;
|
||||
std::float_t lift_distance_mm;
|
||||
std::float_t lift_speed_mms;
|
||||
std::float_t exposure_time_s;
|
||||
std::float_t layer_height_mm;
|
||||
std::float_t layer44; // unkown - usually 0
|
||||
std::float_t layer48; // unkown - usually 0
|
||||
float lift_distance_mm;
|
||||
float lift_speed_mms;
|
||||
float exposure_time_s;
|
||||
float layer_height_mm;
|
||||
float layer44; // unkown - usually 0
|
||||
float layer48; // unkown - usually 0
|
||||
} anycubicsla_format_layer;
|
||||
|
||||
typedef struct anycubicsla_format_misc
|
||||
{
|
||||
std::float_t bottom_layer_height_mm;
|
||||
std::float_t bottom_lift_distance_mm;
|
||||
std::float_t bottom_lift_speed_mms;
|
||||
float bottom_layer_height_mm;
|
||||
float bottom_lift_distance_mm;
|
||||
float bottom_lift_speed_mms;
|
||||
|
||||
} anycubicsla_format_misc;
|
||||
|
||||
@@ -192,9 +192,9 @@ private:
|
||||
|
||||
namespace {
|
||||
|
||||
std::float_t get_cfg_value_f(const DynamicConfig &cfg,
|
||||
float get_cfg_value_f(const DynamicConfig &cfg,
|
||||
const std::string &key,
|
||||
const std::float_t &def = 0.f)
|
||||
const float &def = 0.f)
|
||||
{
|
||||
if (cfg.has(key)) {
|
||||
if (auto opt = cfg.option(key))
|
||||
@@ -276,10 +276,10 @@ void fill_header(anycubicsla_format_header &h,
|
||||
{
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
std::float_t bottle_weight_g;
|
||||
std::float_t bottle_volume_ml;
|
||||
std::float_t bottle_cost;
|
||||
std::float_t material_density;
|
||||
float bottle_weight_g;
|
||||
float bottle_volume_ml;
|
||||
float bottle_cost;
|
||||
float material_density;
|
||||
auto &cfg = print.full_print_config();
|
||||
auto mat_opt = cfg.option("material_notes");
|
||||
std::string mnotes = mat_opt? cfg.option("material_notes")->serialize() : "";
|
||||
@@ -416,7 +416,7 @@ static void anycubicsla_write_int32(std::ofstream &out, std::uint32_t val)
|
||||
out.write((const char *) &i3, 1);
|
||||
out.write((const char *) &i4, 1);
|
||||
}
|
||||
static void anycubicsla_write_float(std::ofstream &out, std::float_t val)
|
||||
static void anycubicsla_write_float(std::ofstream &out, float val)
|
||||
{
|
||||
std::uint32_t *f = (std::uint32_t *) &val;
|
||||
anycubicsla_write_int32(out, *f);
|
||||
|
||||
97
src/libslic3r/Format/SVG.cpp
Normal file
97
src/libslic3r/Format/SVG.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../TriangleMesh.hpp"
|
||||
#include "../NSVGUtils.hpp"
|
||||
#include "../Emboss.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace {
|
||||
std::string get_file_name(const std::string &file_path)
|
||||
{
|
||||
if (file_path.empty())
|
||||
return file_path;
|
||||
|
||||
size_t pos_last_delimiter = file_path.find_last_of("/\\");
|
||||
if (pos_last_delimiter == std::string::npos) {
|
||||
// should not happend that in path is not delimiter
|
||||
assert(false);
|
||||
pos_last_delimiter = 0;
|
||||
}
|
||||
|
||||
size_t pos_point = file_path.find_last_of('.');
|
||||
if (pos_point == std::string::npos || pos_point < pos_last_delimiter // last point is inside of directory path
|
||||
) {
|
||||
// there is no extension
|
||||
assert(false);
|
||||
pos_point = file_path.size();
|
||||
}
|
||||
|
||||
size_t offset = pos_last_delimiter + 1; // result should not contain last delimiter ( +1 )
|
||||
size_t count = pos_point - pos_last_delimiter - 1; // result should not contain extension point ( -1 )
|
||||
return file_path.substr(offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool load_svg(const std::string &input_file, Model &output_model)
|
||||
{
|
||||
EmbossShape::SvgFile svg_file{input_file};
|
||||
const NSVGimage* image = init_image(svg_file);
|
||||
if (image == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "SVG file(\"" << input_file << "\") couldn't be parsed by nano svg.";
|
||||
return false;
|
||||
}
|
||||
|
||||
double tesselation_tolerance = 1e10;
|
||||
NSVGLineParams params(tesselation_tolerance);
|
||||
ExPolygonsWithIds shapes = create_shape_with_ids(*image, params);
|
||||
if (shapes.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "SVG file(\"" << input_file << "\") do not contain embossedabled shape.";
|
||||
return false; // No shapes in svg
|
||||
}
|
||||
|
||||
double depth_in_mm = 10.; // in mm
|
||||
bool use_surface = false;
|
||||
EmbossProjection emboss_projection{depth_in_mm, use_surface};
|
||||
|
||||
EmbossShape emboss_shape;
|
||||
emboss_shape.shapes_with_ids = std::move(shapes);
|
||||
emboss_shape.projection = std::move(emboss_projection);
|
||||
emboss_shape.svg_file = std::move(svg_file);
|
||||
|
||||
// unify to one expolygons
|
||||
// EmbossJob.cpp --> ExPolygons create_shape(DataBase &input, Fnc was_canceled) {
|
||||
ExPolygons union_shape = union_with_delta(emboss_shape, Emboss::UNION_DELTA, Emboss::UNION_MAX_ITERATIN);
|
||||
|
||||
// create projection
|
||||
double scale = emboss_shape.scale;
|
||||
double depth = emboss_shape.projection.depth / scale;
|
||||
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth);
|
||||
Transform3d tr{Eigen::Scaling(scale)};
|
||||
Emboss::ProjectTransform project(std::move(projectZ), tr);
|
||||
|
||||
// convert 2d shape to 3d triangles
|
||||
indexed_triangle_set its = Emboss::polygons2model(union_shape, project);
|
||||
TriangleMesh triangl_mesh(std::move(its));
|
||||
|
||||
// add mesh to model
|
||||
ModelObject *object = output_model.add_object();
|
||||
assert(object != nullptr);
|
||||
if (object == nullptr)
|
||||
return false;
|
||||
object->name = get_file_name(input_file);
|
||||
ModelVolume* volume = object->add_volume(std::move(triangl_mesh));
|
||||
assert(volume != nullptr);
|
||||
if (volume == nullptr) {
|
||||
output_model.delete_object(object);
|
||||
return false;
|
||||
}
|
||||
volume->name = object->name; // copy
|
||||
volume->emboss_shape = std::move(emboss_shape);
|
||||
object->invalidate_bounding_box();
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
14
src/libslic3r/Format/SVG.hpp
Normal file
14
src/libslic3r/Format/SVG.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef slic3r_Format_SVG_hpp_
|
||||
#define slic3r_Format_SVG_hpp_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
// Load an SVG file as embossed shape into a provided model.
|
||||
bool load_svg(const std::string &input_file, Model &output_model);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_SVG_hpp_ */
|
||||
@@ -7,9 +7,20 @@
|
||||
#include "objparser.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
static double strtod_clocale(const char* str, char const** str_end)
|
||||
{
|
||||
double val = 0.;
|
||||
auto [pend, ec] = fast_float::from_chars(str, *str_end, val);
|
||||
if (pend != str && ec != std::errc::result_out_of_range)
|
||||
*str_end = pend; // success
|
||||
else
|
||||
*str_end = str;
|
||||
return val;
|
||||
}
|
||||
static bool obj_parseline(const char *line, ObjData &data)
|
||||
{
|
||||
#define EATWS() while (*line == ' ' || *line == '\t') ++ line
|
||||
@@ -41,15 +52,15 @@ static bool obj_parseline(const char *line, ObjData &data)
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double u = strtod(line, &endptr);
|
||||
const char *endptr = 0;
|
||||
double u = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double v = 0;
|
||||
if (*line != 0) {
|
||||
v = strtod(line, &endptr);
|
||||
v = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
@@ -57,7 +68,7 @@ static bool obj_parseline(const char *line, ObjData &data)
|
||||
}
|
||||
double w = 0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
w = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
@@ -78,18 +89,18 @@ static bool obj_parseline(const char *line, ObjData &data)
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double x = strtod(line, &endptr);
|
||||
const char *endptr = 0;
|
||||
double x = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double y = strtod(line, &endptr);
|
||||
double y = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double z = strtod(line, &endptr);
|
||||
double z = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
@@ -108,20 +119,20 @@ static bool obj_parseline(const char *line, ObjData &data)
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double u = strtod(line, &endptr);
|
||||
const char *endptr = 0;
|
||||
double u = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double v = strtod(line, &endptr);
|
||||
double v = strtod_clocale(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);
|
||||
w = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
@@ -140,25 +151,25 @@ static bool obj_parseline(const char *line, ObjData &data)
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double x = strtod(line, &endptr);
|
||||
const char *endptr = 0;
|
||||
double x = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double y = strtod(line, &endptr);
|
||||
double y = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double z = strtod(line, &endptr);
|
||||
double z = strtod_clocale(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);
|
||||
w = strtod_clocale(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
|
||||
Reference in New Issue
Block a user