Merge remote-tracking branch 'remotes/origin/master' into vb_print_regions
This commit is contained in:
commit
0d081c90f0
46 changed files with 521 additions and 265 deletions
|
@ -192,12 +192,12 @@ sub init_print {
|
|||
if (defined $params{duplicate} && $params{duplicate} > 1) {
|
||||
$model->duplicate($params{duplicate} // 1, $config->min_object_distance);
|
||||
}
|
||||
$model->arrange_objects($config->min_object_distance);
|
||||
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
|
||||
foreach my $model_object (@{$model->objects}) {
|
||||
$model_object->ensure_on_bed;
|
||||
$print->auto_assign_extruders($model_object);
|
||||
}
|
||||
$model->arrange_objects($config->min_object_distance);
|
||||
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
|
||||
|
||||
$print->apply($model, $config);
|
||||
$print->validate;
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include "stl.h"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its)
|
||||
{
|
||||
// 3 indices to vertex per face
|
||||
|
@ -127,6 +129,7 @@ void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its)
|
|||
|
||||
bool its_write_off(const indexed_triangle_set &its, const char *file)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
/* Open the file */
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
|
@ -146,6 +149,7 @@ bool its_write_off(const indexed_triangle_set &its, const char *file)
|
|||
|
||||
bool its_write_vrml(const indexed_triangle_set &its, const char *file)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
/* Open the file */
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
|
@ -188,7 +192,7 @@ bool its_write_vrml(const indexed_triangle_set &its, const char *file)
|
|||
|
||||
bool its_write_obj(const indexed_triangle_set &its, const char *file)
|
||||
{
|
||||
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "stl.h"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#error "SEEK_SET not defined"
|
||||
#endif
|
||||
|
@ -232,6 +234,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
|
|||
|
||||
bool stl_open(stl_file *stl, const char *file)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
stl->clear();
|
||||
FILE *fp = stl_open_count_facets(stl, file);
|
||||
if (fp == nullptr)
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "LocalesUtils.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
@ -376,7 +378,8 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& recent_proje
|
|||
}
|
||||
}
|
||||
|
||||
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed, bool swap_yz)
|
||||
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone,
|
||||
float rotation_speed, float rotation_deadzone, double zoom_speed, bool swap_yz)
|
||||
{
|
||||
std::string key = std::string("mouse_device:") + name;
|
||||
auto it = m_storage.find(key);
|
||||
|
@ -384,11 +387,11 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe
|
|||
it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type(key, std::map<std::string, std::string>())).first;
|
||||
|
||||
it->second.clear();
|
||||
it->second["translation_speed"] = std::to_string(translation_speed);
|
||||
it->second["translation_deadzone"] = std::to_string(translation_deadzone);
|
||||
it->second["rotation_speed"] = std::to_string(rotation_speed);
|
||||
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone);
|
||||
it->second["zoom_speed"] = std::to_string(zoom_speed);
|
||||
it->second["translation_speed"] = float_to_string_decimal_point(translation_speed);
|
||||
it->second["translation_deadzone"] = float_to_string_decimal_point(translation_deadzone);
|
||||
it->second["rotation_speed"] = float_to_string_decimal_point(rotation_speed);
|
||||
it->second["rotation_deadzone"] = float_to_string_decimal_point(rotation_deadzone);
|
||||
it->second["zoom_speed"] = float_to_string_decimal_point(zoom_speed);
|
||||
it->second["swap_yz"] = swap_yz ? "1" : "0";
|
||||
}
|
||||
|
||||
|
|
|
@ -196,6 +196,6 @@ private:
|
|||
bool m_legacy_datadir;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_AppConfig_hpp_ */
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "Arrange.hpp"
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
|
|
|
@ -126,6 +126,8 @@ add_library(libslic3r STATIC
|
|||
Line.hpp
|
||||
LibraryCheck.cpp
|
||||
LibraryCheck.hpp
|
||||
LocalesUtils.cpp
|
||||
LocalesUtils.hpp
|
||||
Model.cpp
|
||||
Model.hpp
|
||||
ModelArrange.hpp
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "Config.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
@ -462,7 +464,7 @@ void ConfigBase::set(const std::string &opt_key, double value, bool create)
|
|||
switch (opt->type()) {
|
||||
case coFloat: static_cast<ConfigOptionFloat*>(opt)->value = value; break;
|
||||
case coFloatOrPercent: static_cast<ConfigOptionFloatOrPercent*>(opt)->value = value; static_cast<ConfigOptionFloatOrPercent*>(opt)->percent = false; break;
|
||||
case coString: static_cast<ConfigOptionString*>(opt)->value = std::to_string(value); break;
|
||||
case coString: static_cast<ConfigOptionString*>(opt)->value = float_to_string_decimal_point(value); break;
|
||||
default: throw BadOptionTypeException("Configbase::set() - conversion from float not possible");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1863,10 +1863,10 @@ public:
|
|||
SetDeserializeItem(const std::string &opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {}
|
||||
std::string opt_key; std::string opt_value; bool append = false;
|
||||
};
|
||||
// May throw BadOptionTypeException() if the operation fails.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "../Exception.hpp"
|
||||
#include "../Model.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../LocalesUtils.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../GCode/ThumbnailData.hpp"
|
||||
|
@ -2408,6 +2409,7 @@ namespace Slic3r {
|
|||
};
|
||||
|
||||
auto format_coordinate = [](float f, char *buf) -> char* {
|
||||
assert(is_decimal_separator_point());
|
||||
#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
|
||||
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
|
||||
// https://github.com/boostorg/spirit/pull/586
|
||||
|
@ -2575,6 +2577,7 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
std::string out = "";
|
||||
char buffer[1024];
|
||||
|
||||
|
@ -2666,6 +2669,7 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
std::string out = "";
|
||||
char buffer[1024];
|
||||
|
||||
|
@ -2700,6 +2704,7 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Exporter::_add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
const char *const fmt = "object_id=%d|";
|
||||
std::string out;
|
||||
|
||||
|
@ -2750,6 +2755,7 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
char buffer[1024];
|
||||
sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
|
||||
std::string out = buffer;
|
||||
|
@ -2926,6 +2932,9 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
|
|||
if (path == nullptr || config == nullptr || model == nullptr)
|
||||
return false;
|
||||
|
||||
// All import should use "C" locales for number formatting.
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
_3MF_Importer importer;
|
||||
bool res = importer.load_model_from_file(path, *model, *config, check_version);
|
||||
importer.log_errors();
|
||||
|
@ -2934,6 +2943,9 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
|
|||
|
||||
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64)
|
||||
{
|
||||
// All export should use "C" locales for number formatting.
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
if (path == nullptr || model == nullptr)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "../I18N.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../CustomGCode.hpp"
|
||||
#include "../LocalesUtils.hpp"
|
||||
|
||||
#include "AMF.hpp"
|
||||
|
||||
|
@ -498,6 +499,7 @@ void AMFParserContext::characters(const XML_Char *s, int len)
|
|||
|
||||
void AMFParserContext::endElement(const char * /* name */)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
switch (m_path.back()) {
|
||||
|
||||
// Constellation transformation:
|
||||
|
@ -1052,6 +1054,8 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
|
|||
// If config is not a null pointer, updates it if the amf file/archive contains config data
|
||||
bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version)
|
||||
{
|
||||
CNumericLocalesSetter locales_setter; // use "C" locales and point as a decimal separator
|
||||
|
||||
if (boost::iends_with(path, ".amf.xml"))
|
||||
// backward compatibility with older slic3r output
|
||||
return load_amf_file(path, config, model);
|
||||
|
@ -1251,40 +1255,25 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
|
|||
stream << " </object>\n";
|
||||
if (!object->instances.empty()) {
|
||||
for (ModelInstance *instance : object->instances) {
|
||||
char buf[512];
|
||||
::sprintf(buf,
|
||||
" <instance objectid=\"%zu\">\n"
|
||||
" <deltax>%lf</deltax>\n"
|
||||
" <deltay>%lf</deltay>\n"
|
||||
" <deltaz>%lf</deltaz>\n"
|
||||
" <rx>%lf</rx>\n"
|
||||
" <ry>%lf</ry>\n"
|
||||
" <rz>%lf</rz>\n"
|
||||
" <scalex>%lf</scalex>\n"
|
||||
" <scaley>%lf</scaley>\n"
|
||||
" <scalez>%lf</scalez>\n"
|
||||
" <mirrorx>%lf</mirrorx>\n"
|
||||
" <mirrory>%lf</mirrory>\n"
|
||||
" <mirrorz>%lf</mirrorz>\n"
|
||||
" <printable>%d</printable>\n"
|
||||
" </instance>\n",
|
||||
object_id,
|
||||
instance->get_offset(X),
|
||||
instance->get_offset(Y),
|
||||
instance->get_offset(Z),
|
||||
instance->get_rotation(X),
|
||||
instance->get_rotation(Y),
|
||||
instance->get_rotation(Z),
|
||||
instance->get_scaling_factor(X),
|
||||
instance->get_scaling_factor(Y),
|
||||
instance->get_scaling_factor(Z),
|
||||
instance->get_mirror(X),
|
||||
instance->get_mirror(Y),
|
||||
instance->get_mirror(Z),
|
||||
instance->printable);
|
||||
std::stringstream buf;
|
||||
buf << " <instance objectid=\"" << object_id << "\">\n"
|
||||
<< " <deltax>" << instance->get_offset(X) << "</deltax>\n"
|
||||
<< " <deltay>" << instance->get_offset(Y) << "</deltay>\n"
|
||||
<< " <deltaz>" << instance->get_offset(Z) << "</deltaz>\n"
|
||||
<< " <rx>" << instance->get_rotation(X) << "</rx>\n"
|
||||
<< " <ry>" << instance->get_rotation(Y) << "</ry>\n"
|
||||
<< " <rz>" << instance->get_rotation(Z) << "</rz>\n"
|
||||
<< " <scalex>" << instance->get_scaling_factor(X) << "</scalex>\n"
|
||||
<< " <scaley>" << instance->get_scaling_factor(Y) << "</scaley>\n"
|
||||
<< " <scalez>" << instance->get_scaling_factor(Z) << "</scalez>\n"
|
||||
<< " <mirrorx>" << instance->get_mirror(X) << "</mirrorx>\n"
|
||||
<< " <mirrory>" << instance->get_mirror(Y) << "</mirrory>\n"
|
||||
<< " <mirrorz>" << instance->get_mirror(Z) << "</mirrorz>\n"
|
||||
<< " <printable>" << instance->printable << "</printable>\n"
|
||||
<< " </instance>\n";
|
||||
|
||||
//FIXME missing instance->scaling_factor
|
||||
instances.append(buf);
|
||||
instances.append(buf.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@ extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model,
|
|||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
|
||||
|
||||
}; // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_AMF_hpp_ */
|
||||
|
|
|
@ -345,6 +345,7 @@ std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key)
|
|||
|
||||
void fill_iniconf(ConfMap &m, const SLAPrint &print)
|
||||
{
|
||||
CNumericLocalesSetter locales_setter; // for to_string
|
||||
auto &cfg = print.full_print_config();
|
||||
m["layerHeight"] = get_cfg_value(cfg, "layer_height");
|
||||
m["expTime"] = get_cfg_value(cfg, "exposure_time");
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "objparser.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
static bool obj_parseline(const char *line, ObjData &data)
|
||||
|
@ -15,6 +17,8 @@ static bool obj_parseline(const char *line, ObjData &data)
|
|||
if (*line == 0)
|
||||
return true;
|
||||
|
||||
assert(Slic3r::is_decimal_separator_point());
|
||||
|
||||
// Ignore whitespaces at the beginning of the line.
|
||||
//FIXME is this a good idea?
|
||||
EATWS();
|
||||
|
@ -322,6 +326,8 @@ static bool obj_parseline(const char *line, ObjData &data)
|
|||
|
||||
bool objparse(const char *path, ObjData &data)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
|
||||
FILE *pFile = boost::nowide::fopen(path, "rt");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
@ -365,6 +371,8 @@ bool objparse(const char *path, ObjData &data)
|
|||
|
||||
bool objparse(std::istream &stream, ObjData &data)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
|
||||
try {
|
||||
char buf[65536 * 2];
|
||||
size_t len = 0;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "Utils.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
@ -750,6 +751,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
std::string path_tmp(path);
|
||||
path_tmp += ".tmp";
|
||||
|
||||
CNumericLocalesSetter c_locales_setter;
|
||||
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
||||
if (file == nullptr)
|
||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||
|
@ -981,6 +983,7 @@ namespace DoExport {
|
|||
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
|
||||
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
|
||||
auto append = [&extruder](std::pair<std::string, unsigned int> &dst, const char *tmpl, double value) {
|
||||
assert(is_decimal_separator_point());
|
||||
while (dst.second < extruder.id()) {
|
||||
// Fill in the non-printing extruders with zeros.
|
||||
dst.first += (dst.second > 0) ? ", 0" : "0";
|
||||
|
@ -1620,7 +1623,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
|
|||
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
|
||||
travel_acc);
|
||||
|
||||
|
||||
assert(is_decimal_separator_point());
|
||||
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
print.config().machine_max_jerk_x.values.front(),
|
||||
print.config().machine_max_jerk_y.values.front(),
|
||||
|
@ -1984,6 +1987,7 @@ void GCode::process_layer(
|
|||
}
|
||||
|
||||
std::string gcode;
|
||||
assert(is_decimal_separator_point()); // for the sprintfs
|
||||
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
@ -2826,6 +2830,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
// so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines
|
||||
bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower);
|
||||
char buf[64];
|
||||
assert(is_decimal_separator_point());
|
||||
|
||||
if (path.role() != m_last_processor_extrusion_role) {
|
||||
m_last_processor_extrusion_role = path.role();
|
||||
|
|
|
@ -357,6 +357,8 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||
for (; *c == ' ' || *c == '\t'; ++ c);
|
||||
if (*c == 0 || *c == ';')
|
||||
break;
|
||||
|
||||
assert(is_decimal_separator_point()); // for atof
|
||||
// Parse the axis.
|
||||
size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') :
|
||||
(*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
|
||||
|
@ -454,6 +456,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||
line.type = CoolingLine::TYPE_G4;
|
||||
size_t pos_S = sline.find('S', 3);
|
||||
size_t pos_P = sline.find('P', 3);
|
||||
assert(is_decimal_separator_point()); // for atof
|
||||
line.time = line.time_max = float(
|
||||
(pos_S > 0) ? atof(sline.c_str() + pos_S + 1) :
|
||||
(pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
@ -465,9 +466,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
};
|
||||
|
||||
auto format_time_float = [](float time) {
|
||||
char time_str[64];
|
||||
sprintf(time_str, "%.2f", time);
|
||||
return std::string(time_str);
|
||||
return Slic3r::float_to_string_decimal_point(time, 2);
|
||||
};
|
||||
|
||||
auto format_line_M73_stop_float = [format_time_float](const std::string& mask, float time) {
|
||||
|
@ -671,7 +670,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
|
||||
std::string time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
std::string next_time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - next_it->elapsed_time));
|
||||
is_last |= (std::stof(time_float_str) > 0.0f && std::stof(next_time_float_str) == 0.0f);
|
||||
is_last |= (string_to_double_decimal_point(time_float_str) > 0. && string_to_double_decimal_point(next_time_float_str) == 0.);
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
|
@ -1374,7 +1373,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
if (pos != cmt.npos) {
|
||||
pos = cmt.find(',', pos);
|
||||
if (pos != cmt.npos) {
|
||||
out = std::stod(cmt.substr(pos + 1));
|
||||
out = string_to_double_decimal_point(cmt.substr(pos+1));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1524,9 +1523,9 @@ template<typename T>
|
|||
else if constexpr (std::is_same_v<T, long>)
|
||||
out = std::stol(str, &read);
|
||||
else if constexpr (std::is_same_v<T, float>)
|
||||
out = std::stof(str, &read);
|
||||
out = string_to_double_decimal_point(str, &read);
|
||||
else if constexpr (std::is_same_v<T, double>)
|
||||
out = std::stod(str, &read);
|
||||
out = string_to_double_decimal_point(str, &read);
|
||||
return str.size() == read;
|
||||
} catch (...) {
|
||||
return false;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../LocalesUtils.hpp"
|
||||
|
||||
#include "PressureEqualizer.hpp"
|
||||
|
||||
|
@ -158,7 +159,7 @@ static inline int parse_int(const char *&line)
|
|||
static inline float parse_float(const char *&line)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
float result = strtof(line, &endptr);
|
||||
float result = string_to_double_decimal_point(line, &endptr);
|
||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
||||
line = endptr;
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "GCodeProcessor.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
|
||||
namespace Slic3r
|
||||
|
@ -30,29 +33,27 @@ public:
|
|||
m_filpar(filament_parameters)
|
||||
{
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
std::ostringstream str;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
m_gcode += buf;
|
||||
sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str());
|
||||
str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << ExtrusionEntity::role_to_string(erWipeTower) << "\n";
|
||||
#else
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
m_gcode += buf;
|
||||
sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str());
|
||||
str << ";" << GCodeProcessor::Height_Tag << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
str << ";" << GCodeProcessor::Extrusion_Role_Tag << ExtrusionEntity::role_to_string(erWipeTower) << "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_gcode += buf;
|
||||
m_gcode += str.str();
|
||||
change_analyzer_line_width(line_width);
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
std::stringstream str;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width).c_str(), line_width);
|
||||
str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) << line_width << "\n";
|
||||
#else
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width);
|
||||
str << ";" << GCodeProcessor::Width_Tag << line_width << "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_gcode += buf;
|
||||
m_gcode += str.str();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -61,9 +62,9 @@ public:
|
|||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for processor:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
std::stringstream str;
|
||||
str << ";" << GCodeProcessor::Mm3_Per_Mm_Tag << mm3_per_mm << "\n";
|
||||
m_gcode += str.str();
|
||||
return *this;
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
@ -325,9 +326,7 @@ public:
|
|||
{
|
||||
if (time==0.f)
|
||||
return *this;
|
||||
char buf[128];
|
||||
sprintf(buf, "G4 S%.3f\n", time);
|
||||
m_gcode += buf;
|
||||
m_gcode += "G4 S" + Slic3r::float_to_string_decimal_point(time, 3) + "\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -444,29 +443,21 @@ private:
|
|||
|
||||
std::string set_format_X(float x)
|
||||
{
|
||||
char buf[64];
|
||||
sprintf(buf, " X%.3f", x);
|
||||
m_current_pos.x() = x;
|
||||
return buf;
|
||||
return " X" + Slic3r::float_to_string_decimal_point(x, 3);
|
||||
}
|
||||
|
||||
std::string set_format_Y(float y) {
|
||||
char buf[64];
|
||||
sprintf(buf, " Y%.3f", y);
|
||||
m_current_pos.y() = y;
|
||||
return buf;
|
||||
return " Y" + Slic3r::float_to_string_decimal_point(y, 3);
|
||||
}
|
||||
|
||||
std::string set_format_Z(float z) {
|
||||
char buf[64];
|
||||
sprintf(buf, " Z%.3f", z);
|
||||
return buf;
|
||||
return " Z" + Slic3r::float_to_string_decimal_point(z, 3);
|
||||
}
|
||||
|
||||
std::string set_format_E(float e) {
|
||||
char buf[64];
|
||||
sprintf(buf, " E%.4f", e);
|
||||
return buf;
|
||||
return " E" + Slic3r::float_to_string_decimal_point(e, 4);
|
||||
}
|
||||
|
||||
std::string set_format_F(float f) {
|
||||
|
@ -959,8 +950,8 @@ void WipeTower::toolchange_Change(
|
|||
// postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before.
|
||||
Vec2f current_pos = writer.pos_rotated();
|
||||
writer.feedrate(m_travel_speed * 60.f) // see https://github.com/prusa3d/PrusaSlicer/issues/5483
|
||||
.append(std::string("G1 X") + std::to_string(current_pos.x())
|
||||
+ " Y" + std::to_string(current_pos.y())
|
||||
.append(std::string("G1 X") + Slic3r::float_to_string_decimal_point(current_pos.x())
|
||||
+ " Y" + Slic3r::float_to_string_decimal_point(current_pos.y())
|
||||
+ never_skip_tag() + "\n");
|
||||
|
||||
// The toolchange Tn command will be inserted later, only in case that the user does
|
||||
|
@ -1310,11 +1301,10 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
|
|||
{
|
||||
assert(first.new_tool == second.initial_tool);
|
||||
WipeTower::ToolChangeResult out = first;
|
||||
if (first.end_pos != second.start_pos) {
|
||||
char buf[2048]; // Add a travel move from tc1.end_pos to tc2.start_pos.
|
||||
sprintf(buf, "G1 X%.3f Y%.3f F7200\n", second.start_pos.x(), second.start_pos.y());
|
||||
out.gcode += buf;
|
||||
}
|
||||
if (first.end_pos != second.start_pos)
|
||||
out.gcode += "G1 X" + Slic3r::float_to_string_decimal_point(second.start_pos.x(), 3)
|
||||
+ " Y" + Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3)
|
||||
+ " F7200\n";
|
||||
out.gcode += second.gcode;
|
||||
out.extrusions.insert(out.extrusions.end(), second.extrusions.begin(), second.extrusions.end());
|
||||
out.end_pos = second.end_pos;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -25,6 +27,7 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config)
|
|||
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CNumericLocalesSetter locales_setter; // for strtod
|
||||
|
||||
// command and args
|
||||
const char *c = ptr;
|
||||
|
@ -150,6 +153,7 @@ bool GCodeReader::GCodeLine::has(char axis) const
|
|||
|
||||
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
{
|
||||
CNumericLocalesSetter locales_setter; // for strtod
|
||||
const char *c = m_raw.c_str();
|
||||
// Skip the whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
|
|
76
src/libslic3r/LocalesUtils.cpp
Normal file
76
src/libslic3r/LocalesUtils.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "LocalesUtils.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
CNumericLocalesSetter::CNumericLocalesSetter()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||
m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
#else
|
||||
m_original_locale = uselocale((locale_t)0);
|
||||
m_new_locale = duplocale(m_original_locale);
|
||||
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
|
||||
uselocale(m_new_locale);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
CNumericLocalesSetter::~CNumericLocalesSetter()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::setlocale(LC_NUMERIC, m_orig_numeric_locale.data());
|
||||
#else
|
||||
uselocale(m_original_locale);
|
||||
freelocale(m_new_locale);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool is_decimal_separator_point()
|
||||
{
|
||||
char str[5] = "";
|
||||
sprintf(str, "%.1f", 0.5f);
|
||||
return str[1] == '.';
|
||||
}
|
||||
|
||||
|
||||
double string_to_double_decimal_point(const std::string& str, size_t* pos /* = nullptr*/)
|
||||
{
|
||||
double out;
|
||||
std::istringstream stream(str);
|
||||
if (! (stream >> out))
|
||||
throw std::invalid_argument("string_to_double_decimal_point conversion failed.");
|
||||
if (pos) {
|
||||
if (stream.eof())
|
||||
*pos = str.size();
|
||||
else
|
||||
*pos = stream.tellg();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string float_to_string_decimal_point(double value, int precision/* = -1*/)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
std::stringstream buf;
|
||||
if (precision >= 0)
|
||||
buf << std::fixed << std::setprecision(precision);
|
||||
buf << value;
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
//std::string float_to_string_decimal_point(float value, int precision/* = -1*/)
|
||||
//{
|
||||
// return float_to_string_decimal_point(double(value), precision);
|
||||
//}
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
47
src/libslic3r/LocalesUtils.hpp
Normal file
47
src/libslic3r/LocalesUtils.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef slic3r_LocalesUtils_hpp_
|
||||
#define slic3r_LocalesUtils_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <clocale>
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <xlocale.h>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// RAII wrapper that sets LC_NUMERIC to "C" on construction
|
||||
// and restores the old value on destruction.
|
||||
class CNumericLocalesSetter {
|
||||
public:
|
||||
CNumericLocalesSetter();
|
||||
~CNumericLocalesSetter();
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
std::string m_orig_numeric_locale;
|
||||
#else
|
||||
locale_t m_original_locale;
|
||||
locale_t m_new_locale;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
// A function to check that current C locale uses decimal point as a separator.
|
||||
// Intended mostly for asserts.
|
||||
bool is_decimal_separator_point();
|
||||
|
||||
|
||||
// A substitute for std::to_string that works according to
|
||||
// C++ locales, not C locale. Meant to be used when we need
|
||||
// to be sure that decimal point is used as a separator.
|
||||
// (We use user C locales and "C" C++ locales in most of the code.)
|
||||
std::string float_to_string_decimal_point(double value, int precision = -1);
|
||||
//std::string float_to_string_decimal_point(float value, int precision = -1);
|
||||
double string_to_double_decimal_point(const std::string& str, size_t* pos = nullptr);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_LocalesUtils_hpp_
|
|
@ -1,3 +1,4 @@
|
|||
#include "libslic3r.h"
|
||||
#include "Exception.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
|
@ -889,35 +890,22 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
|
||||
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
|
||||
// This method is used by the auto arrange function.
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
||||
{
|
||||
Points pts;
|
||||
for (const ModelVolume* v : volumes) {
|
||||
if (v->is_model_part())
|
||||
append(pts, its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f).points);
|
||||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
}
|
||||
#else
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
|
||||
{
|
||||
Points pts;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
const Transform3d trafo = trafo_instance * v->get_matrix();
|
||||
const TriangleMesh& hull_3d = v->get_convex_hull();
|
||||
const indexed_triangle_set& its = hull_3d.its;
|
||||
if (its.vertices.empty()) {
|
||||
// Using the STL faces.
|
||||
const stl_file& stl = hull_3d.stl;
|
||||
for (const stl_facet& facet : stl.facet_start) {
|
||||
for (size_t j = 0; j < 3; ++j) {
|
||||
const Vec3d p = trafo * facet.vertex[j].cast<double>();
|
||||
if (p.z() >= 0.0)
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i) {
|
||||
const Vec3d p = trafo * its.vertices[i].cast<double>();
|
||||
if (p.z() >= 0.0)
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
#else
|
||||
Transform3d trafo = trafo_instance * v->get_matrix();
|
||||
const indexed_triangle_set &its = v->mesh().its;
|
||||
if (its.vertices.empty()) {
|
||||
|
@ -935,10 +923,10 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
|
|||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
}
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
void ModelObject::center_around_origin(bool include_modifiers)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
|
@ -88,10 +90,10 @@ inline Vec3d unscale(coord_t x, coord_t y, coord_t z) { return Vec3d(unscale<d
|
|||
inline Vec3d unscale(const Vec3crd &pt) { return Vec3d(unscale<double>(pt.x()), unscale<double>(pt.y()), unscale<double>(pt.z())); }
|
||||
inline Vec3d unscale(const Vec3d &pt) { return Vec3d(unscale<double>(pt.x()), unscale<double>(pt.y()), unscale<double>(pt.z())); }
|
||||
|
||||
inline std::string to_string(const Vec2crd &pt) { return std::string("[") + std::to_string(pt.x()) + ", " + std::to_string(pt.y()) + "]"; }
|
||||
inline std::string to_string(const Vec2d &pt) { return std::string("[") + std::to_string(pt.x()) + ", " + std::to_string(pt.y()) + "]"; }
|
||||
inline std::string to_string(const Vec3crd &pt) { return std::string("[") + std::to_string(pt.x()) + ", " + std::to_string(pt.y()) + ", " + std::to_string(pt.z()) + "]"; }
|
||||
inline std::string to_string(const Vec3d &pt) { return std::string("[") + std::to_string(pt.x()) + ", " + std::to_string(pt.y()) + ", " + std::to_string(pt.z()) + "]"; }
|
||||
inline std::string to_string(const Vec2crd &pt) { return std::string("[") + float_to_string_decimal_point(pt.x()) + ", " + float_to_string_decimal_point(pt.y()) + "]"; }
|
||||
inline std::string to_string(const Vec2d &pt) { return std::string("[") + float_to_string_decimal_point(pt.x()) + ", " + float_to_string_decimal_point(pt.y()) + "]"; }
|
||||
inline std::string to_string(const Vec3crd &pt) { return std::string("[") + float_to_string_decimal_point(pt.x()) + ", " + float_to_string_decimal_point(pt.y()) + ", " + float_to_string_decimal_point(pt.z()) + "]"; }
|
||||
inline std::string to_string(const Vec3d &pt) { return std::string("[") + float_to_string_decimal_point(pt.x()) + ", " + float_to_string_decimal_point(pt.y()) + ", " + float_to_string_decimal_point(pt.z()) + "]"; }
|
||||
|
||||
std::vector<Vec3f> transform(const std::vector<Vec3f>& points, const Transform3f& t);
|
||||
Pointf3s transform(const Pointf3s& points, const Transform3d& t);
|
||||
|
|
|
@ -166,8 +166,8 @@ struct FaceHash {
|
|||
Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3;
|
||||
|
||||
// Return a concatenated string representation of the coordinates
|
||||
return std::to_string(c(0)) + std::to_string(c(1)) + std::to_string(c(2));
|
||||
};
|
||||
return float_to_string_decimal_point(c(0)) + float_to_string_decimal_point(c(1)) + float_to_string_decimal_point(c(2));
|
||||
}
|
||||
|
||||
FaceHash(const indexed_triangle_set &its)
|
||||
{
|
||||
|
|
|
@ -611,7 +611,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
|||
return output_mesh;
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z)
|
||||
std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z) const
|
||||
{
|
||||
// convert doubles to floats
|
||||
std::vector<float> z_f(z.begin(), z.end());
|
||||
|
@ -911,16 +911,16 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c
|
|||
all_pts.reserve(all_pts.size() + its.indices.size() * 3);
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const Vec3f pts[3] = { transform_fn(its.vertices[tri(0)]), transform_fn(its.vertices[tri(1)]), transform_fn(its.vertices[tri(2)]) };
|
||||
int iprev = 3;
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge) {
|
||||
const Vec3f &p1 = pts[iprev];
|
||||
const Vec3f &p2 = pts[iedge];
|
||||
if ((p1.z() < z && p2.z() > z) || (p2.z() < z && p1.z() > z)) {
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
float t = z / (p2.z() - p1.z());
|
||||
all_pts.emplace_back(scaled<coord_t>(p1.x() + (p2.x() - p1.x()) * t), scaled<coord_t>(p2.x() + (p2.y() - p2.y()) * t));
|
||||
float t = (z - p1.z()) / (p2.z() - p1.z());
|
||||
all_pts.emplace_back(scaled<coord_t>(p1.x() + (p2.x() - p1.x()) * t), scaled<coord_t>(p1.y() + (p2.y() - p1.y()) * t));
|
||||
}
|
||||
if (p2.z() > z)
|
||||
if (p2.z() >= z)
|
||||
all_pts.emplace_back(scaled<coord_t>(p2.x()), scaled<coord_t>(p2.y()));
|
||||
iprev = iedge;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
// Returns the convex hull of this TriangleMesh
|
||||
TriangleMesh convex_hull_3d() const;
|
||||
// Slice this mesh at the provided Z levels and return the vector
|
||||
std::vector<ExPolygons> slice(const std::vector<double>& z);
|
||||
std::vector<ExPolygons> slice(const std::vector<double>& z) const;
|
||||
void reset_repair_stats();
|
||||
bool needed_repair() const;
|
||||
void require_shared_vertices();
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
|
||||
#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
|
||||
#include "SVG.hpp"
|
||||
|
|
|
@ -5953,17 +5953,16 @@ static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64
|
|||
|
||||
static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
|
||||
{
|
||||
(void)pZip;
|
||||
memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, (pZip->m_pState->m_zip64) ? 0x002D : (method ? 0x0014 : 0x0000));
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || comp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size);
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || uncomp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
|
||||
return MZ_TRUE;
|
||||
|
@ -5975,10 +5974,12 @@ static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_
|
|||
mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,
|
||||
mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
|
||||
{
|
||||
(void)pZip;
|
||||
mz_zip_internal_state *pState = pZip->m_pState;
|
||||
mz_bool is_zip64_needed = uncomp_size >= MZ_UINT32_MAX || comp_size >= MZ_UINT32_MAX;
|
||||
memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
|
||||
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000));
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000));
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
|
||||
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
|
||||
|
@ -6675,6 +6676,15 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
|
|||
return MZ_TRUE;
|
||||
}
|
||||
|
||||
static mz_uint32 mz_zip_writer_preallocate_extra_data(mz_uint8 *pBuf, mz_uint32 extra_data_size)
|
||||
{
|
||||
mz_uint8 *pDst = pBuf;
|
||||
MZ_WRITE_LE16(pDst + 0, 0x9999);
|
||||
MZ_WRITE_LE16(pDst + 2, extra_data_size);
|
||||
memset(pDst + 4, 0, extra_data_size);
|
||||
return 2 * sizeof(mz_uint16) + extra_data_size;
|
||||
}
|
||||
|
||||
mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged_context* pContext, const char* pArchive_name, mz_uint64 max_size, const MZ_TIME_T* pFile_time, const void* pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
||||
const char* user_extra_data, mz_uint user_extra_data_len, const char* user_extra_data_central, mz_uint user_extra_data_central_len)
|
||||
{
|
||||
|
@ -6708,13 +6718,6 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged
|
|||
|
||||
pState = pZip->m_pState;
|
||||
|
||||
if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX))
|
||||
{
|
||||
/* Source file is too large for non-zip64 */
|
||||
/*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
|
||||
pState->m_zip64 = MZ_TRUE;
|
||||
}
|
||||
|
||||
/* We could support this, but why? */
|
||||
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
|
||||
|
@ -6763,6 +6766,12 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged
|
|||
{
|
||||
mz_zip_time_t_to_dos_time(*pFile_time, &pContext->dos_time, &pContext->dos_date);
|
||||
}
|
||||
else
|
||||
{
|
||||
MZ_TIME_T cur_time;
|
||||
time(&cur_time);
|
||||
mz_zip_time_t_to_dos_time(cur_time, &pContext->dos_time, &pContext->dos_date);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mz_zip_writer_write_zeros(pZip, pContext->cur_archive_file_ofs, num_alignment_padding_bytes))
|
||||
|
@ -6778,14 +6787,22 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged
|
|||
MZ_ASSERT((pContext->cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
|
||||
}
|
||||
|
||||
if (!pState->m_zip64 && (pContext->local_dir_header_ofs >= MZ_UINT32_MAX)) {
|
||||
pState->m_zip64 = MZ_TRUE;
|
||||
/*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
|
||||
}
|
||||
|
||||
MZ_CLEAR_OBJ(local_dir_header);
|
||||
if (pState->m_zip64)
|
||||
{
|
||||
if (max_size >= MZ_UINT32_MAX || pContext->local_dir_header_ofs >= MZ_UINT32_MAX)
|
||||
pContext->pExtra_data = pContext->extra_data;
|
||||
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size,
|
||||
(pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
|
||||
}
|
||||
else if (!pState->m_zip64 && max_size > MZ_UINT32_MAX)
|
||||
{
|
||||
pContext->pExtra_data = pContext->extra_data;
|
||||
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, (max_size >= MZ_UINT32_MAX) ? &pContext->uncomp_size : NULL,
|
||||
(max_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
|
||||
pContext->extra_size = mz_zip_writer_preallocate_extra_data(pContext->extra_data, 2 * sizeof(mz_uint64));
|
||||
}
|
||||
|
||||
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)(pContext->extra_size + user_extra_data_len), 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date))
|
||||
|
@ -6803,30 +6820,12 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged
|
|||
|
||||
pContext->cur_archive_file_ofs += pContext->archive_name_size;
|
||||
|
||||
if (pState->m_zip64 || max_size >= MZ_UINT32_MAX) {
|
||||
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pContext->extra_data, pContext->extra_size) != pContext->extra_size)
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
|
||||
pContext->cur_archive_file_ofs += pContext->extra_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pContext->comp_size > MZ_UINT32_MAX) || (pContext->cur_archive_file_ofs > MZ_UINT32_MAX))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
|
||||
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
|
||||
|
||||
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
|
||||
pContext->cur_archive_file_ofs += sizeof(local_dir_header);
|
||||
|
||||
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size)
|
||||
{
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
}
|
||||
|
||||
pContext->cur_archive_file_ofs += pContext->archive_name_size;
|
||||
}
|
||||
|
||||
if (user_extra_data_len > 0)
|
||||
{
|
||||
|
@ -6898,17 +6897,40 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext)
|
|||
pContext->pZip->m_pFree(pContext->pZip->m_pAlloc_opaque, pContext->pCompressor);
|
||||
pContext->pCompressor = NULL;
|
||||
|
||||
// Rewrite preallocated phony custom block in local dir header by ZIP64 extension. Also, other values are adjusted in the header.
|
||||
if (pContext->file_ofs >= MZ_UINT32_MAX || pContext->add_state.m_comp_size >= MZ_UINT32_MAX) {
|
||||
mz_uint64 local_dir_header_ofs = pContext->local_dir_header_ofs;
|
||||
pContext->pExtra_data = pContext->extra_data;
|
||||
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
|
||||
|
||||
mz_uint8 min_version[2] = {0x2D, 0x00};
|
||||
mz_uint8 comp_uncomp_size[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_VERSION_NEEDED_OFS, min_version, sizeof(min_version)) != sizeof(min_version))
|
||||
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_uncomp_size, sizeof(comp_uncomp_size)) != sizeof(comp_uncomp_size))
|
||||
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, comp_uncomp_size, sizeof(comp_uncomp_size)) != sizeof(comp_uncomp_size))
|
||||
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + pContext->archive_name_size, pContext->extra_data, pContext->extra_size) != pContext->extra_size)
|
||||
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
|
||||
}
|
||||
|
||||
pContext->uncomp_size = pContext->file_ofs;
|
||||
pContext->comp_size = pContext->add_state.m_comp_size;
|
||||
pContext->cur_archive_file_ofs = pContext->add_state.m_cur_archive_file_ofs;
|
||||
|
||||
// Clean pContext->pExtra_data, pContext->extra_data and pContext->extra_size after calling mz_zip_writer_add_staged_open
|
||||
pContext->pExtra_data = NULL;
|
||||
pContext->extra_size = 0;
|
||||
memset(pContext->extra_data, 0, sizeof(mz_uint8) * MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE);
|
||||
|
||||
{
|
||||
mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
|
||||
mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;
|
||||
|
||||
MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
|
||||
MZ_WRITE_LE32(local_dir_footer + 4, pContext->uncomp_crc32);
|
||||
if (pContext->pExtra_data == NULL)
|
||||
if (pContext->uncomp_size < MZ_UINT32_MAX && pContext->comp_size < MZ_UINT32_MAX)
|
||||
{
|
||||
if (pContext->comp_size > MZ_UINT32_MAX)
|
||||
return mz_zip_set_error(pContext->pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
|
||||
|
@ -6929,10 +6951,11 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext)
|
|||
pContext->cur_archive_file_ofs += local_dir_footer_size;
|
||||
}
|
||||
|
||||
if (pContext->pExtra_data != NULL)
|
||||
if (pContext->file_ofs >= MZ_UINT32_MAX || pContext->add_state.m_comp_size >= MZ_UINT32_MAX)
|
||||
{
|
||||
pContext->pExtra_data = pContext->extra_data;
|
||||
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, (pContext->uncomp_size >= MZ_UINT32_MAX) ? &pContext->uncomp_size : NULL,
|
||||
(pContext->uncomp_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
|
||||
(pContext->comp_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
|
||||
}
|
||||
|
||||
if (!mz_zip_writer_add_to_central_dir(pContext->pZip, pContext->pArchive_name, (mz_uint16)pContext->archive_name_size, pContext->pExtra_data, (mz_uint16)pContext->extra_size, pContext->pComment, pContext->comment_size,
|
||||
|
|
|
@ -266,7 +266,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
|
|||
std::string bitmap_key = bitmap_name + ( target_height !=0 ?
|
||||
"-h" + std::to_string(target_height) :
|
||||
"-w" + std::to_string(target_width))
|
||||
+ (m_scale != 1.0f ? "-s" + std::to_string(m_scale) : "")
|
||||
+ (m_scale != 1.0f ? "-s" + float_to_string_decimal_point(m_scale) : "")
|
||||
+ (grayscale ? "-gs" : "");
|
||||
|
||||
/* For the Dark mode of any platform, we should draw icons in respect to OS background
|
||||
|
|
|
@ -1388,17 +1388,21 @@ static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
|
|||
{
|
||||
e.Skip();
|
||||
wxString str = ctrl->GetValue();
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
bool was_replace = str.Replace(",", ".", false) > 0;
|
||||
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
bool was_replaced = str.Replace(dec_sep_alt, dec_sep, false) != 0;
|
||||
|
||||
double val = 0.0;
|
||||
if (!str.ToCDouble(&val)) {
|
||||
if (!str.ToDouble(&val)) {
|
||||
if (val == 0.0)
|
||||
val = def_value;
|
||||
ctrl->SetValue(double_to_string(val));
|
||||
show_error(nullptr, _L("Invalid numeric input."));
|
||||
ctrl->SetFocus();
|
||||
}
|
||||
else if (was_replace)
|
||||
else if (was_replaced)
|
||||
ctrl->SetValue(double_to_string(val));
|
||||
}
|
||||
|
||||
|
@ -1447,17 +1451,17 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
|||
void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
|
||||
{
|
||||
double val = 0.0;
|
||||
diam_nozzle->GetValue().ToCDouble(&val);
|
||||
diam_nozzle->GetValue().ToDouble(&val);
|
||||
auto *opt_nozzle = new ConfigOptionFloats(1, val);
|
||||
config.set_key_value("nozzle_diameter", opt_nozzle);
|
||||
|
||||
val = 0.0;
|
||||
diam_filam->GetValue().ToCDouble(&val);
|
||||
diam_filam->GetValue().ToDouble(&val);
|
||||
auto * opt_filam = new ConfigOptionFloats(1, val);
|
||||
config.set_key_value("filament_diameter", opt_filam);
|
||||
|
||||
auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) {
|
||||
char buf[64];
|
||||
char buf[64]; // locales don't matter here (sprintf/atof)
|
||||
sprintf(buf, "%.2lf", dmr * opt_nozzle->values.front() / 0.4);
|
||||
config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf), false));
|
||||
};
|
||||
|
|
|
@ -2162,7 +2162,7 @@ static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0,
|
|||
bool disable = textctrl->IsEmpty();
|
||||
if (!disable && min >= 0.0 && max >= 0.0) {
|
||||
double value = -1.0;
|
||||
if (!textctrl->GetValue().ToCDouble(&value)) // input value couldn't be converted to double
|
||||
if (!textctrl->GetValue().ToDouble(&value)) // input value couldn't be converted to double
|
||||
disable = true;
|
||||
else
|
||||
disable = value < min - epsilon() || value > max + epsilon(); // is input value is out of valid range ?
|
||||
|
@ -2231,7 +2231,7 @@ static double get_value_to_jump(double active_value, double min_z, double max_z,
|
|||
return -1.0;
|
||||
|
||||
double value = -1.0;
|
||||
return dlg.GetValue().ToCDouble(&value) ? value : -1.0;
|
||||
return dlg.GetValue().ToDouble(&value) ? value : -1.0;
|
||||
}
|
||||
|
||||
void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/)
|
||||
|
|
|
@ -98,9 +98,14 @@ ExtruderSequenceDialog::ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequ
|
|||
return;
|
||||
}
|
||||
|
||||
str.Replace(",", ".", false);
|
||||
char dec_sep = '.';
|
||||
if (! is_decimal_separator_point()) {
|
||||
str.Replace(".", ",", false);
|
||||
dec_sep = ',';
|
||||
}
|
||||
|
||||
double val;
|
||||
if (str == "." || !str.ToCDouble(&val) || val <= 0.0)
|
||||
if (str == dec_sep || !str.ToDouble(&val) || val <= 0.0)
|
||||
val = 3.0; // default value
|
||||
|
||||
if (fabs(m_sequence.interval_by_layers - val) < 0.001)
|
||||
|
|
|
@ -37,12 +37,13 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/)
|
|||
// with the exception that here one sets the decimal separator explicitely to dot.
|
||||
// If number is in scientific format, trailing zeroes belong to the exponent and cannot be removed.
|
||||
if (s.find_first_of("eE") == wxString::npos) {
|
||||
const size_t posDecSep = s.find(".");
|
||||
char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const size_t posDecSep = s.find(dec_sep);
|
||||
// No decimal point => removing trailing zeroes irrelevant for integer number.
|
||||
if (posDecSep != wxString::npos) {
|
||||
// Find the last character to keep.
|
||||
size_t posLastNonZero = s.find_last_not_of("0");
|
||||
// If it's the decimal separator itself, don't keep it neither.
|
||||
// If it's the decimal separator itself, don't keep it either.
|
||||
if (posLastNonZero == posDecSep)
|
||||
-- posLastNonZero;
|
||||
s.erase(posLastNonZero + 1);
|
||||
|
@ -237,15 +238,21 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
break;
|
||||
}
|
||||
double val;
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
str.Replace(",", ".", false);
|
||||
if (str == ".")
|
||||
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
set_value(str, false);
|
||||
|
||||
|
||||
if (str == dec_sep)
|
||||
val = 0.0;
|
||||
else
|
||||
{
|
||||
if (m_opt.nullable && str == na_value())
|
||||
val = ConfigOptionFloatsNullable::nil_value();
|
||||
else if (!str.ToCDouble(&val))
|
||||
else if (!str.ToDouble(&val))
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
|
@ -293,14 +300,18 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%')
|
||||
{
|
||||
double val = 0.;
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
str.Replace(",", ".", false);
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
set_value(str, false);
|
||||
|
||||
|
||||
// remove space and "mm" substring, if any exists
|
||||
str.Replace(" ", "", true);
|
||||
str.Replace("m", "", true);
|
||||
|
||||
if (!str.ToCDouble(&val))
|
||||
if (!str.ToDouble(&val))
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
|
@ -1215,8 +1226,8 @@ boost::any& Choice::get_value()
|
|||
return m_value;
|
||||
}
|
||||
|
||||
void Choice::enable() { dynamic_cast<choice_ctrl*>(window)->Enable(); };
|
||||
void Choice::disable() { dynamic_cast<choice_ctrl*>(window)->Disable(); };
|
||||
void Choice::enable() { dynamic_cast<choice_ctrl*>(window)->Enable(); }
|
||||
void Choice::disable() { dynamic_cast<choice_ctrl*>(window)->Disable(); }
|
||||
|
||||
void Choice::msw_rescale()
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
@ -78,6 +79,7 @@ static float round_to_nearest(float value, unsigned int decimals)
|
|||
res = std::round(value);
|
||||
else {
|
||||
char buf[64];
|
||||
// locales should not matter, both sprintf and stof are sensitive, so...
|
||||
sprintf(buf, "%.*g", decimals, value);
|
||||
res = std::stof(buf);
|
||||
}
|
||||
|
@ -990,6 +992,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||
// save materials file
|
||||
boost::filesystem::path mat_filename(filename);
|
||||
mat_filename.replace_extension("mtl");
|
||||
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing";
|
||||
|
@ -3465,18 +3470,16 @@ void GCodeViewer::render_statistics() const
|
|||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
|
||||
auto add_time = [this, &imgui](const std::string& label, int64_t time) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%lld ms (%s)", time, get_time_dhms(static_cast<float>(time) * 0.001f).c_str());
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label);
|
||||
ImGui::SameLine(offset);
|
||||
imgui.text(buf);
|
||||
imgui.text(std::to_string(time) + " ms (" + get_time_dhms(static_cast<float>(time) * 0.001f) + ")");
|
||||
};
|
||||
|
||||
auto add_memory = [this, &imgui](const std::string& label, int64_t memory) {
|
||||
auto format_string = [memory](const std::string& units, float value) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%lld bytes (%.3f %s)", memory, static_cast<float>(memory) * value, units.c_str());
|
||||
return std::string(buf);
|
||||
return std::to_string(memory) + " bytes (" +
|
||||
Slic3r::float_to_string_decimal_point(float(memory) * value, 3)
|
||||
+ " " + units + ")";
|
||||
};
|
||||
|
||||
static const float kb = 1024.0f;
|
||||
|
@ -3496,11 +3499,9 @@ void GCodeViewer::render_statistics() const
|
|||
};
|
||||
|
||||
auto add_counter = [this, &imgui](const std::string& label, int64_t counter) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%lld", counter);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label);
|
||||
ImGui::SameLine(offset);
|
||||
imgui.text(buf);
|
||||
imgui.text(std::to_string(counter));
|
||||
};
|
||||
|
||||
imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f);
|
||||
|
|
|
@ -1356,7 +1356,7 @@ void GLCanvas3D::render()
|
|||
if (m_rectangle_selection.is_dragging())
|
||||
// picking pass using rectangle selection
|
||||
_rectangular_selection_picking_pass();
|
||||
else
|
||||
else if (!m_volumes.empty())
|
||||
// regular picking pass
|
||||
_picking_pass();
|
||||
}
|
||||
|
@ -3800,7 +3800,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
|
|||
if (imgui->slider_float(_L("Spacing"), &settings.distance, dist_min, 100.0f, "%5.2f") || dist_min > settings.distance) {
|
||||
settings.distance = std::max(dist_min, settings.distance);
|
||||
settings_out.distance = settings.distance;
|
||||
appcfg->set("arrange", dist_key.c_str(), std::to_string(settings_out.distance));
|
||||
appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance));
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
|
@ -3815,7 +3815,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
|
|||
if (imgui->button(_L("Reset"))) {
|
||||
settings_out = ArrangeSettings{};
|
||||
settings_out.distance = std::max(dist_min, settings_out.distance);
|
||||
appcfg->set("arrange", dist_key.c_str(), std::to_string(settings_out.distance));
|
||||
appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance));
|
||||
appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0");
|
||||
settings_changed = true;
|
||||
}
|
||||
|
@ -4527,6 +4527,14 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
|||
if (m_canvas == nullptr && m_context == nullptr)
|
||||
return;
|
||||
|
||||
#if ENABLE_SCROLLABLE_LEGEND
|
||||
const std::array<unsigned int, 2> new_size = { w, h };
|
||||
if (m_old_size == new_size)
|
||||
return;
|
||||
|
||||
m_old_size = new_size;
|
||||
#endif // ENABLE_SCROLLABLE_LEGEND
|
||||
|
||||
auto *imgui = wxGetApp().imgui();
|
||||
imgui->set_display_size(static_cast<float>(w), static_cast<float>(h));
|
||||
const float font_size = 1.5f * wxGetApp().em_unit();
|
||||
|
|
|
@ -425,6 +425,10 @@ private:
|
|||
Model* m_model;
|
||||
BackgroundSlicingProcess *m_process;
|
||||
|
||||
#if ENABLE_SCROLLABLE_LEGEND
|
||||
std::array<unsigned int, 2> m_old_size{ 0, 0 };
|
||||
#endif // ENABLE_SCROLLABLE_LEGEND
|
||||
|
||||
// Screen is only refreshed from the OnIdle handler if it is dirty.
|
||||
bool m_dirty;
|
||||
bool m_initialized;
|
||||
|
@ -733,6 +737,10 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_SCROLLABLE_LEGEND
|
||||
void reset_old_size() { m_old_size = { 0, 0 }; }
|
||||
#endif // ENABLE_SCROLLABLE_LEGEND
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
@ -113,7 +115,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
|||
str.pop_back();
|
||||
percent = true;
|
||||
}
|
||||
double val = stod(str);
|
||||
double val = std::stod(str); // locale-dependent (on purpose - the input is the actual content of the field)
|
||||
config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent));
|
||||
break;}
|
||||
case coPercent:
|
||||
|
|
|
@ -1586,7 +1586,7 @@ bool GUI_App::load_language(wxString language, bool initial)
|
|||
m_wxLocale->AddCatalog(SLIC3R_APP_KEY);
|
||||
m_imgui->set_language(into_u8(language_info->CanonicalName));
|
||||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
wxSetlocale(LC_NUMERIC, "C");
|
||||
//wxSetlocale(LC_NUMERIC, "C");
|
||||
Preset::update_suffix_modified((" (" + _L("modified") + ")").ToUTF8().data());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -397,12 +397,16 @@ coordf_t LayerRangeEditor::get_value()
|
|||
wxString str = GetValue();
|
||||
|
||||
coordf_t layer_height;
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
str.Replace(",", ".", false);
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
SetValue(str);
|
||||
|
||||
if (str == ".")
|
||||
layer_height = 0.0;
|
||||
else {
|
||||
if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) {
|
||||
if (!str.ToDouble(&layer_height) || layer_height < 0.0f) {
|
||||
show_error(m_parent, _L("Invalid numeric input."));
|
||||
SetValue(double_to_string(layer_height));
|
||||
}
|
||||
|
|
|
@ -1095,15 +1095,19 @@ double ManipulationEditor::get_value()
|
|||
wxString str = GetValue();
|
||||
|
||||
double value;
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
str.Replace(",", ".", false);
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
SetValue(str);
|
||||
|
||||
if (str == ".")
|
||||
value = 0.0;
|
||||
|
||||
if ((str.IsEmpty() || !str.ToCDouble(&value)) && !m_valid_value.IsEmpty()) {
|
||||
if ((str.IsEmpty() || !str.ToDouble(&value)) && !m_valid_value.IsEmpty()) {
|
||||
str = m_valid_value;
|
||||
SetValue(str);
|
||||
str.ToCDouble(&value);
|
||||
str.ToDouble(&value);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "PrintHostDialogs.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
@ -304,8 +305,8 @@ void NotificationManager::PopNotification::count_lines()
|
|||
}
|
||||
// hypertext calculation
|
||||
if (!m_hypertext.empty()) {
|
||||
int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0;
|
||||
if (ImGui::CalcTextSize((text.substr(prev_end, last_end - prev_end) + m_hypertext).c_str()).x > m_window_width - m_window_width_offset) {
|
||||
int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0; // m_endlines.size() - 2 because we are fitting hypertext instead of last endline
|
||||
if (ImGui::CalcTextSize((escape_string_cstyle(text.substr(prev_end, last_end - prev_end)) + m_hypertext).c_str()).x > m_window_width - m_window_width_offset) {
|
||||
m_endlines.push_back(last_end);
|
||||
m_lines_count++;
|
||||
}
|
||||
|
@ -366,7 +367,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
|||
}
|
||||
//hyperlink text
|
||||
if (!m_hypertext.empty()) {
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3437,8 +3437,12 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||
|
||||
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
|
||||
view3D->set_as_dirty();
|
||||
#if ENABLE_SCROLLABLE_LEGEND
|
||||
// reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size
|
||||
view3D->get_canvas3d()->reset_old_size();
|
||||
#endif // ENABLE_SCROLLABLE_LEGEND
|
||||
view_toolbar.select_item("3D");
|
||||
if(notification_manager != nullptr)
|
||||
if (notification_manager != nullptr)
|
||||
notification_manager->set_in_preview(false);
|
||||
}
|
||||
else if (current_panel == preview) {
|
||||
|
@ -3457,6 +3461,10 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||
preview->reload_print(true);
|
||||
|
||||
preview->set_as_dirty();
|
||||
#if ENABLE_SCROLLABLE_LEGEND
|
||||
// reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size
|
||||
preview->get_canvas3d()->reset_old_size();
|
||||
#endif // ENABLE_SCROLLABLE_LEGEND
|
||||
view_toolbar.select_item("Preview");
|
||||
if (notification_manager != nullptr)
|
||||
notification_manager->set_in_preview(true);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Camera.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
@ -1927,7 +1928,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
|||
if (pos == std::string::npos)
|
||||
return;
|
||||
|
||||
double max_z = std::stod(field.substr(pos + 1));
|
||||
double max_z = string_to_double_decimal_point(field.substr(pos + 1));
|
||||
|
||||
// extract min_z
|
||||
field = field.substr(0, pos);
|
||||
|
@ -1935,7 +1936,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
|||
if (pos == std::string::npos)
|
||||
return;
|
||||
|
||||
const double min_z = std::stod(field.substr(pos + 1));
|
||||
const double min_z = string_to_double_decimal_point(field.substr(pos + 1));
|
||||
|
||||
// extract type
|
||||
field = field.substr(0, pos);
|
||||
|
|
|
@ -30,23 +30,23 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
|
|||
load_stl(src_file.c_str(), &src_model);
|
||||
src_model.add_default_instances();
|
||||
|
||||
ModelObject* src_object = src_model.objects[0];
|
||||
ModelObject* src_object = src_model.objects.front();
|
||||
|
||||
// apply generic transformation to the 1st volume
|
||||
Geometry::Transformation src_volume_transform;
|
||||
src_volume_transform.set_offset(Vec3d(10.0, 20.0, 0.0));
|
||||
src_volume_transform.set_rotation(Vec3d(Geometry::deg2rad(25.0), Geometry::deg2rad(35.0), Geometry::deg2rad(45.0)));
|
||||
src_volume_transform.set_scaling_factor(Vec3d(1.1, 1.2, 1.3));
|
||||
src_volume_transform.set_mirror(Vec3d(-1.0, 1.0, -1.0));
|
||||
src_object->volumes[0]->set_transformation(src_volume_transform);
|
||||
src_volume_transform.set_offset({ 10.0, 20.0, 0.0 });
|
||||
src_volume_transform.set_rotation({ Geometry::deg2rad(25.0), Geometry::deg2rad(35.0), Geometry::deg2rad(45.0) });
|
||||
src_volume_transform.set_scaling_factor({ 1.1, 1.2, 1.3 });
|
||||
src_volume_transform.set_mirror({ -1.0, 1.0, -1.0 });
|
||||
src_object->volumes.front()->set_transformation(src_volume_transform);
|
||||
|
||||
// apply generic transformation to the 1st instance
|
||||
Geometry::Transformation src_instance_transform;
|
||||
src_instance_transform.set_offset(Vec3d(5.0, 10.0, 0.0));
|
||||
src_instance_transform.set_rotation(Vec3d(Geometry::deg2rad(12.0), Geometry::deg2rad(13.0), Geometry::deg2rad(14.0)));
|
||||
src_instance_transform.set_scaling_factor(Vec3d(0.9, 0.8, 0.7));
|
||||
src_instance_transform.set_mirror(Vec3d(1.0, -1.0, -1.0));
|
||||
src_object->instances[0]->set_transformation(src_instance_transform);
|
||||
src_instance_transform.set_offset({ 5.0, 10.0, 0.0 });
|
||||
src_instance_transform.set_rotation({ Geometry::deg2rad(12.0), Geometry::deg2rad(13.0), Geometry::deg2rad(14.0) });
|
||||
src_instance_transform.set_scaling_factor({ 0.9, 0.8, 0.7 });
|
||||
src_instance_transform.set_mirror({ 1.0, -1.0, -1.0 });
|
||||
src_object->instances.front()->set_transformation(src_instance_transform);
|
||||
|
||||
WHEN("model is saved+loaded to/from 3mf file") {
|
||||
// save the model to 3mf file
|
||||
|
@ -67,16 +67,9 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
|
|||
dst_mesh.repair();
|
||||
|
||||
bool res = src_mesh.its.vertices.size() == dst_mesh.its.vertices.size();
|
||||
if (res)
|
||||
{
|
||||
for (size_t i = 0; i < dst_mesh.its.vertices.size(); ++i)
|
||||
{
|
||||
if (res) {
|
||||
for (size_t i = 0; i < dst_mesh.its.vertices.size(); ++i) {
|
||||
res &= dst_mesh.its.vertices[i].isApprox(src_mesh.its.vertices[i]);
|
||||
if (!res)
|
||||
{
|
||||
Vec3f diff = dst_mesh.its.vertices[i] - src_mesh.its.vertices[i];
|
||||
std::cout << i << ": diff " << to_string((Vec3d)diff.cast<double>()) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
THEN("world vertices coordinates after load match") {
|
||||
|
@ -85,3 +78,45 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("2D convex hull of sinking object", "[3mf]") {
|
||||
GIVEN("model") {
|
||||
// load a model
|
||||
Model model;
|
||||
std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/Prusa.stl";
|
||||
load_stl(src_file.c_str(), &model);
|
||||
model.add_default_instances();
|
||||
|
||||
WHEN("model is rotated, scaled and set as sinking") {
|
||||
ModelObject* object = model.objects.front();
|
||||
object->center_around_origin(false);
|
||||
|
||||
// set instance's attitude so that it is rotated, scaled and sinking
|
||||
ModelInstance* instance = object->instances.front();
|
||||
instance->set_rotation(X, -M_PI / 4.0);
|
||||
instance->set_offset(Vec3d::Zero());
|
||||
instance->set_scaling_factor({ 2.0, 2.0, 2.0 });
|
||||
|
||||
// calculate 2D convex hull
|
||||
Polygon hull_2d = object->convex_hull_2d(instance->get_transformation().get_matrix());
|
||||
|
||||
// verify result
|
||||
Points result = {
|
||||
{ -91501496, -15914144 },
|
||||
{ 91501496, -15914144 },
|
||||
{ 91501496, 4243 },
|
||||
{ 78229680, 4246883 },
|
||||
{ 56898100, 4246883 },
|
||||
{ -85501496, 4242641 },
|
||||
{ -91501496, 4243 }
|
||||
};
|
||||
|
||||
bool res = hull_2d.points == result;
|
||||
|
||||
THEN("2D convex hull should match with reference") {
|
||||
REQUIRE(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
|
@ -112,7 +113,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
|||
WHEN("A string option is set through the double interface") {
|
||||
config.set("end_gcode", 100.5);
|
||||
THEN("The underlying value is set correctly.") {
|
||||
REQUIRE(config.opt<ConfigOptionString>("end_gcode")->value == std::to_string(100.5));
|
||||
REQUIRE(config.opt<ConfigOptionString>("end_gcode")->value == float_to_string_decimal_point(100.5));
|
||||
}
|
||||
}
|
||||
WHEN("A float or percent is set as a percent through the string interface.") {
|
||||
|
|
Loading…
Reference in a new issue