Merge remote-tracking branch 'origin/master' into ys_optgroup_refact

This commit is contained in:
YuSanka 2020-09-23 08:38:21 +02:00
commit fc3aa61dc4
115 changed files with 3693 additions and 2589 deletions

View file

@ -589,7 +589,7 @@ int CLI::run(int argc, char **argv)
#if ENABLE_GCODE_VIEWER
if (start_as_gcodeviewer) {
if (!m_input_files.empty())
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0]));
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str()));
} else {
#endif // ENABLE_GCODE_VIEWER_AS
#if 0

View file

@ -255,18 +255,24 @@ extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t, bool fix_left_handed = false)
{
//const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
for (stl_vertex &v : its.vertices)
v = (t * v.template cast<T>()).template cast<float>().eval();
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.)
for (stl_triangle_vertex_indices &i : its.indices)
std::swap(i[0], i[1]);
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m, bool fix_left_handed = false)
{
for (stl_vertex &v : its.vertices)
for (stl_vertex &v : its.vertices)
v = (m * v.template cast<T>()).template cast<float>().eval();
if (fix_left_handed && m.determinant() < 0.)
for (stl_triangle_vertex_indices &i : its.indices)
std::swap(i[0], i[1]);
}
extern void its_rotate_x(indexed_triangle_set &its, float angle);

View file

@ -283,7 +283,7 @@ namespace detail {
template<typename V, typename W>
std::enable_if_t<std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()),
const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()),
&t, &u, &v);
@ -291,7 +291,7 @@ namespace detail {
template<typename V, typename W>
std::enable_if_t<std::is_same<typename V::Scalar, double>::value && !std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector w0 = v0.template cast<double>();
Vector w1 = v1.template cast<double>();
@ -302,7 +302,7 @@ namespace detail {
template<typename V, typename W>
std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector o = origin.template cast<double>();
Vector d = dir.template cast<double>();
@ -311,7 +311,7 @@ namespace detail {
template<typename V, typename W>
std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && ! std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector o = origin.template cast<double>();
Vector d = dir.template cast<double>();

View file

@ -1,6 +1,7 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp"
#include "AppConfig.hpp"
#include "Exception.hpp"
#include <utility>
#include <vector>
@ -126,7 +127,7 @@ std::string AppConfig::load()
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
// we will rethrow this exception from the place of load() call, if returned value wouldn't be empty
/*
throw std::runtime_error(
throw Slic3r::RuntimeError(
_utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. "
"Try to manually delete the file to recover from the error. Your user profiles will not be affected.")) +
"\n\n" + AppConfig::config_path() + "\n\n" + ex.what());

View file

@ -75,6 +75,7 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
}
}
template void BoundingBoxBase<Point>::merge(const Point &point);
template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
template <class PointClass> void
@ -101,6 +102,7 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
}
}
template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb);
template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
template <class PointClass> void
@ -115,6 +117,7 @@ BoundingBox3Base<PointClass>::merge(const PointClass &point)
this->defined = true;
}
}
template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
template <class PointClass> void
@ -147,6 +150,7 @@ BoundingBoxBase<PointClass>::size() const
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1));
}
template Point BoundingBoxBase<Point>::size() const;
template Vec2f BoundingBoxBase<Vec2f>::size() const;
template Vec2d BoundingBoxBase<Vec2d>::size() const;
template <class PointClass> PointClass
@ -154,6 +158,7 @@ BoundingBox3Base<PointClass>::size() const
{
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2));
}
template Vec3f BoundingBox3Base<Vec3f>::size() const;
template Vec3d BoundingBox3Base<Vec3d>::size() const;
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const
@ -200,6 +205,7 @@ BoundingBoxBase<PointClass>::center() const
return (this->min + this->max) / 2;
}
template Point BoundingBoxBase<Point>::center() const;
template Vec2f BoundingBoxBase<Vec2f>::center() const;
template Vec2d BoundingBoxBase<Vec2d>::center() const;
template <class PointClass> PointClass
@ -207,6 +213,7 @@ BoundingBox3Base<PointClass>::center() const
{
return (this->min + this->max) / 2;
}
template Vec3f BoundingBox3Base<Vec3f>::center() const;
template Vec3d BoundingBox3Base<Vec3d>::center() const;
template <class PointClass> coordf_t
@ -215,6 +222,7 @@ BoundingBox3Base<PointClass>::max_size() const
PointClass s = size();
return std::max(s(0), std::max(s(1), s(2)));
}
template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
// Align a coordinate to a grid. The coordinate may be negative,

View file

@ -2,6 +2,7 @@
#define slic3r_BoundingBox_hpp_
#include "libslic3r.h"
#include "Exception.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
@ -18,11 +19,13 @@ public:
BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {}
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {}
BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
BoundingBoxBase(const std::vector<PointClass>& points) : min(PointClass::Zero()), max(PointClass::Zero())
{
if (points.empty()) {
this->defined = false;
// throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor");
// throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBoxBase constructor");
} else {
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min = *it;
@ -65,10 +68,12 @@ public:
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
BoundingBoxBase<PointClass>(pmin, pmax)
{ if (pmin(2) >= pmax(2)) BoundingBoxBase<PointClass>::defined = false; }
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); }
BoundingBox3Base(const std::vector<PointClass>& points)
{
if (points.empty())
throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor");
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min = *it;
this->max = *it;
@ -109,24 +114,32 @@ extern template void BoundingBoxBase<Vec3d>::scale(double factor);
extern template void BoundingBoxBase<Point>::offset(coordf_t delta);
extern template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
extern template void BoundingBoxBase<Point>::merge(const Point &point);
extern template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
extern template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
extern template void BoundingBoxBase<Point>::merge(const Points &points);
extern template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
extern template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb);
extern template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
extern template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
extern template Point BoundingBoxBase<Point>::size() const;
extern template Vec2f BoundingBoxBase<Vec2f>::size() const;
extern template Vec2d BoundingBoxBase<Vec2d>::size() const;
extern template double BoundingBoxBase<Point>::radius() const;
extern template double BoundingBoxBase<Vec2d>::radius() const;
extern template Point BoundingBoxBase<Point>::center() const;
extern template Vec2f BoundingBoxBase<Vec2f>::center() const;
extern template Vec2d BoundingBoxBase<Vec2d>::center() const;
extern template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
extern template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
extern template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
extern template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb);
extern template Vec3f BoundingBox3Base<Vec3f>::size() const;
extern template Vec3d BoundingBox3Base<Vec3d>::size() const;
extern template double BoundingBox3Base<Vec3d>::radius() const;
extern template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
extern template Vec3f BoundingBox3Base<Vec3f>::center() const;
extern template Vec3d BoundingBox3Base<Vec3d>::center() const;
extern template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
extern template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
class BoundingBox : public BoundingBoxBase<Point>

View file

@ -97,6 +97,8 @@ add_library(libslic3r STATIC
GCode/PrintExtents.hpp
GCode/SpiralVase.cpp
GCode/SpiralVase.hpp
GCode/SeamPlacer.cpp
GCode/SeamPlacer.hpp
GCode/ToolOrdering.cpp
GCode/ToolOrdering.hpp
GCode/WipeTower.cpp

View file

@ -5,7 +5,6 @@
#include <fstream>
#include <iostream>
#include <iomanip>
#include <exception> // std::runtime_error
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/erase.hpp>
@ -218,7 +217,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
case coInts: return new ConfigOptionIntsNullable();
case coPercents: return new ConfigOptionPercentsNullable();
case coBools: return new ConfigOptionBoolsNullable();
default: throw std::runtime_error(std::string("Unknown option type for nullable option ") + this->label);
default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label);
}
} else {
switch (this->type) {
@ -238,7 +237,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
case coBool: return new ConfigOptionBool();
case coBools: return new ConfigOptionBools();
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label);
default: throw Slic3r::RuntimeError(std::string("Unknown option type for option ") + this->label);
}
}
}
@ -535,7 +534,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
return opt_def->ratio_over.empty() ? 0. :
static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
}
throw std::runtime_error("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
}
// Return an absolute value of a possibly relative config variable.
@ -546,7 +545,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
const ConfigOption *raw_opt = this->option(opt_key);
assert(raw_opt != nullptr);
if (raw_opt->type() != coFloatOrPercent)
throw std::runtime_error("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
// Compute absolute value.
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
}
@ -609,7 +608,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file)
std::getline(ifs, firstline);
if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 &&
strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0)
throw std::runtime_error("Not a PrusaSlicer / Slic3r PE generated g-code.");
throw Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code.");
}
ifs.seekg(0, ifs.end);
auto file_length = ifs.tellg();
@ -621,7 +620,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file)
size_t key_value_pairs = load_from_gcode_string(data.data());
if (key_value_pairs < 80)
throw std::runtime_error(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
}
// Load the config keys from the given string.
@ -750,7 +749,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
throw NoDefinitionException(opt_key);
const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr)
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
// throw Slic3r::RuntimeError(std::string("Invalid option name: ") + opt_key);
// Let the parent decide what to do if the opt_key is not defined by this->def().
return nullptr;
ConfigOption *opt = optdef->create_default_option();

View file

@ -13,6 +13,7 @@
#include <vector>
#include "libslic3r.h"
#include "clonable_ptr.hpp"
#include "Exception.hpp"
#include "Point.hpp"
#include <boost/algorithm/string/trim.hpp>
@ -34,31 +35,31 @@ extern bool unescape_string_cstyle(const std::string &str, std::string &
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::runtime_error {
class UnknownOptionException : public Slic3r::RuntimeError {
public:
UnknownOptionException() :
std::runtime_error("Unknown option exception") {}
Slic3r::RuntimeError("Unknown option exception") {}
UnknownOptionException(const std::string &opt_key) :
std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {}
};
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
class NoDefinitionException : public std::runtime_error
class NoDefinitionException : public Slic3r::RuntimeError
{
public:
NoDefinitionException() :
std::runtime_error("No definition exception") {}
Slic3r::RuntimeError("No definition exception") {}
NoDefinitionException(const std::string &opt_key) :
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
Slic3r::RuntimeError(std::string("No definition exception: ") + opt_key) {}
};
/// Indicate that an unsupported accessor was called on a config option.
class BadOptionTypeException : public std::runtime_error
class BadOptionTypeException : public Slic3r::RuntimeError
{
public:
BadOptionTypeException() : std::runtime_error("Bad option type exception") {}
BadOptionTypeException(const std::string &message) : std::runtime_error(message) {}
BadOptionTypeException(const char* message) : std::runtime_error(message) {}
BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {}
BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {}
BadOptionTypeException(const char* message) : Slic3r::RuntimeError(message) {}
};
// Type of a configuration value.
@ -167,7 +168,7 @@ public:
void set(const ConfigOption *rhs) override
{
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionSingle: Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
}
@ -175,7 +176,7 @@ public:
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
throw std::runtime_error("ConfigOptionSingle: Comparing incompatible types");
throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs));
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
}
@ -239,7 +240,7 @@ public:
void set(const ConfigOption *rhs) override
{
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionVector: Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
}
@ -256,12 +257,12 @@ public:
if (opt->type() == this->type()) {
auto other = static_cast<const ConfigOptionVector<T>*>(opt);
if (other->values.empty())
throw std::runtime_error("ConfigOptionVector::set(): Assigning from an empty vector");
throw Slic3r::RuntimeError("ConfigOptionVector::set(): Assigning from an empty vector");
this->values.emplace_back(other->values.front());
} else if (opt->type() == this->scalar_type())
this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
else
throw std::runtime_error("ConfigOptionVector::set():: Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type");
}
}
@ -280,12 +281,12 @@ public:
// Assign the first value of the rhs vector.
auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
if (other->values.empty())
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning from an empty vector");
throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning from an empty vector");
this->values[i] = other->get_at(j);
} else if (rhs->type() == this->scalar_type())
this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
else
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning an incompatible type");
}
const T& get_at(size_t i) const
@ -310,9 +311,9 @@ public:
else if (n > this->values.size()) {
if (this->values.empty()) {
if (opt_default == nullptr)
throw std::runtime_error("ConfigOptionVector::resize(): No default value provided.");
throw Slic3r::RuntimeError("ConfigOptionVector::resize(): No default value provided.");
if (opt_default->type() != this->type())
throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type.");
throw Slic3r::RuntimeError("ConfigOptionVector::resize(): Extending with an incompatible type.");
this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front());
} else {
// Resize by duplicating the last value.
@ -329,7 +330,7 @@ public:
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
throw std::runtime_error("ConfigOptionVector: Comparing incompatible types");
throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs));
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
}
@ -341,9 +342,9 @@ public:
// An option overrides another option if it is not nil and not equal.
bool overriden_by(const ConfigOption *rhs) const override {
if (this->nullable())
throw std::runtime_error("Cannot override a nullable ConfigOption.");
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption.");
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionVector.overriden_by() applied to different types.");
throw Slic3r::RuntimeError("ConfigOptionVector.overriden_by() applied to different types.");
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable())
// Overridding a non-nullable object with another non-nullable object.
@ -361,9 +362,9 @@ public:
// Apply an override option, possibly a nullable one.
bool apply_override(const ConfigOption *rhs) override {
if (this->nullable())
throw std::runtime_error("Cannot override a nullable ConfigOption.");
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption.");
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types.");
throw Slic3r::RuntimeError("ConfigOptionVector.apply_override() applied to different types.");
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable()) {
// Overridding a non-nullable object with another non-nullable object.
@ -452,7 +453,7 @@ public:
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type())
throw std::runtime_error("ConfigOptionFloatsTempl: Comparing incompatible types");
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
}
@ -499,7 +500,7 @@ public:
if (NULLABLE)
this->values.push_back(nil_value());
else
throw std::runtime_error("Deserializing nil into a non-nullable object");
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
} else {
std::istringstream iss(item_str);
double value;
@ -524,9 +525,9 @@ protected:
if (NULLABLE)
ss << "nil";
else
throw std::runtime_error("Serializing NaN");
throw Slic3r::RuntimeError("Serializing NaN");
} else
throw std::runtime_error("Serializing invalid number");
throw Slic3r::RuntimeError("Serializing invalid number");
}
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
if (NULLABLE) {
@ -645,7 +646,7 @@ public:
if (NULLABLE)
this->values.push_back(nil_value());
else
throw std::runtime_error("Deserializing nil into a non-nullable object");
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
} else {
std::istringstream iss(item_str);
int value;
@ -662,7 +663,7 @@ private:
if (NULLABLE)
ss << "nil";
else
throw std::runtime_error("Serializing NaN");
throw Slic3r::RuntimeError("Serializing NaN");
} else
ss << v;
}
@ -847,7 +848,7 @@ public:
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
throw std::runtime_error("ConfigOptionFloatOrPercent: Comparing incompatible types");
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
}
@ -858,7 +859,7 @@ public:
void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionFloatOrPercent: Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
*this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
}
@ -1126,7 +1127,7 @@ public:
if (NULLABLE)
this->values.push_back(nil_value());
else
throw std::runtime_error("Deserializing nil into a non-nullable object");
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
} else
this->values.push_back(item_str.compare("1") == 0);
}
@ -1139,7 +1140,7 @@ protected:
if (NULLABLE)
ss << "nil";
else
throw std::runtime_error("Serializing NaN");
throw Slic3r::RuntimeError("Serializing NaN");
} else
ss << (v ? "1" : "0");
}
@ -1175,14 +1176,14 @@ public:
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
throw std::runtime_error("ConfigOptionEnum<T>: Comparing incompatible types");
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Comparing incompatible types");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
return this->value == (T)rhs.getInt();
}
void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionEnum<T>: Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
this->value = (T)rhs->getInt();
}
@ -1259,14 +1260,14 @@ public:
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
throw std::runtime_error("ConfigOptionEnumGeneric: Comparing incompatible types");
throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Comparing incompatible types");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
return this->value == rhs.getInt();
}
void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionEnumGeneric: Assigning an incompatible type");
throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
this->value = rhs->getInt();
}
@ -1321,7 +1322,7 @@ public:
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
}
} else {
switch (this->type) {
@ -1340,7 +1341,7 @@ public:
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
}
}
}
@ -1352,7 +1353,7 @@ public:
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
}
} else {
switch (this->type) {
@ -1371,7 +1372,7 @@ public:
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
}
}
// Make the compiler happy, shut up the warnings.

View file

@ -1,5 +1,6 @@
#include "BoundingBox.hpp"
#include "ExPolygon.hpp"
#include "Exception.hpp"
#include "Geometry.hpp"
#include "Polygon.hpp"
#include "Line.hpp"
@ -435,7 +436,7 @@ void ExPolygon::triangulate_pp(Polygons* polygons) const
std::list<TPPLPoly> output;
int res = TPPLPartition().Triangulate_MONO(&input, &output);
if (res != 1)
throw std::runtime_error("Triangulation failed");
throw Slic3r::RuntimeError("Triangulation failed");
// convert output polygons
for (std::list<TPPLPoly>::iterator poly = output.begin(); poly != output.end(); ++poly) {
@ -548,7 +549,7 @@ void ExPolygon::triangulate_pp(Points *triangles) const
int res = TPPLPartition().Triangulate_MONO(&input, &output);
// int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
if (res != 1)
throw std::runtime_error("Triangulation failed");
throw Slic3r::RuntimeError("Triangulation failed");
*triangles = polypartition_output_to_triangles(output);
}
@ -591,7 +592,7 @@ void ExPolygon::triangulate_p2t(Polygons* polygons) const
}
polygons->push_back(p);
}
} catch (const std::runtime_error & /* err */) {
} catch (const Slic3r::RuntimeError & /* err */) {
assert(false);
// just ignore, don't triangulate
}

View file

@ -0,0 +1,28 @@
#ifndef _libslic3r_Exception_h_
#define _libslic3r_Exception_h_
#include <stdexcept>
namespace Slic3r {
// PrusaSlicer's own exception hierarchy is derived from std::runtime_error.
// Base for Slicer's own exceptions.
class Exception : public std::runtime_error { using std::runtime_error::runtime_error; };
#define SLIC3R_DERIVE_EXCEPTION(DERIVED_EXCEPTION, PARENT_EXCEPTION) \
class DERIVED_EXCEPTION : public PARENT_EXCEPTION { using PARENT_EXCEPTION::PARENT_EXCEPTION; }
// Critical exception produced by Slicer, such exception shall never propagate up to the UI thread.
// If that happens, an ugly fat message box with an ugly fat exclamation mark is displayed.
SLIC3R_DERIVE_EXCEPTION(CriticalException, Exception);
SLIC3R_DERIVE_EXCEPTION(RuntimeError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(LogicError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(InvalidArgument, LogicError);
SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError);
SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(FileIOError, IOError);
// Runtime exception produced by Slicer. Such exception cancels the slicing process and it shall be shown in notifications.
SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception);
#undef SLIC3R_DERIVE_EXCEPTION
} // namespace Slic3r
#endif // _libslic3r_Exception_h_

View file

@ -2,6 +2,7 @@
#define slic3r_ExtrusionEntityCollection_hpp_
#include "libslic3r.h"
#include "Exception.hpp"
#include "ExtrusionEntity.hpp"
namespace Slic3r {
@ -107,7 +108,7 @@ public:
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const override {
throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection");
throw Slic3r::RuntimeError("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();
};
@ -117,7 +118,7 @@ public:
}
double length() const override {
throw std::runtime_error("Calling length() on a ExtrusionEntityCollection");
throw Slic3r::RuntimeError("Calling length() on a ExtrusionEntityCollection");
return 0.;
}
};

View file

@ -10,14 +10,14 @@
namespace Slic3r {
// Generic file parser error, mostly copied from boost::property_tree::file_parser_error
class file_parser_error: public std::runtime_error
class file_parser_error: public Slic3r::RuntimeError
{
public:
file_parser_error(const std::string &msg, const std::string &file, unsigned long line = 0) :
std::runtime_error(format_what(msg, file, line)),
Slic3r::RuntimeError(format_what(msg, file, line)),
m_message(msg), m_filename(file), m_line(line) {}
file_parser_error(const std::string &msg, const boost::filesystem::path &file, unsigned long line = 0) :
std::runtime_error(format_what(msg, file.string(), line)),
Slic3r::RuntimeError(format_what(msg, file.string(), line)),
m_message(msg), m_filename(file.string()), m_line(line) {}
// gcc 3.4.2 complains about lack of throw specifier on compiler
// generated dtor
@ -35,7 +35,7 @@ private:
std::string m_filename;
unsigned long m_line;
// Format error message to be returned by std::runtime_error::what()
// Format error message to be returned by Slic3r::RuntimeError::what()
static std::string format_what(const std::string &msg, const std::string &file, unsigned long l)
{
std::stringstream stream;

View file

@ -318,7 +318,7 @@ void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill>
#endif
// friend to Layer
void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, FillAdaptive_Internal::Octree* support_fill_octree)
void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree)
{
for (LayerRegion *layerm : m_regions)
layerm->fills.clear();
@ -345,8 +345,7 @@ void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, Fill
f->layer_id = this->id();
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->adapt_fill_octree = adaptive_fill_octree;
f->support_fill_octree = support_fill_octree;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
// calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;

File diff suppressed because it is too large Load diff

View file

@ -1,138 +1,75 @@
// Adaptive cubic infill was inspired by the work of @mboerwinkle
// as implemented for Cura.
// https://github.com/Ultimaker/CuraEngine/issues/381
// https://github.com/Ultimaker/CuraEngine/pull/401
//
// Our implementation is more accurate (discretizes a bit less cubes than Cura's)
// by splitting only such cubes which contain a triangle.
// Our line extraction is time optimal instead of O(n^2) when connecting extracted lines,
// and we also implemented adaptivity for supporting internal overhangs only.
#ifndef slic3r_FillAdaptive_hpp_
#define slic3r_FillAdaptive_hpp_
#include "../AABBTreeIndirect.hpp"
#include "FillBase.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class PrintObject;
namespace FillAdaptive_Internal
namespace FillAdaptive
{
struct CubeProperties
{
double edge_length; // Lenght of edge of a cube
double height; // Height of rotated cube (standing on the corner)
double diagonal_length; // Length of diagonal of a cube a face
double line_z_distance; // Defines maximal distance from a center of a cube on Z axis on which lines will be created
double line_xy_distance;// Defines maximal distance from a center of a cube on X and Y axis on which lines will be created
};
struct Cube
{
Vec3d center;
std::unique_ptr<Cube> children[8] = {};
Cube(const Vec3d &center) : center(center) {}
};
struct Octree;
// To keep the definition of Octree opaque, we have to define a custom deleter.
struct OctreeDeleter { void operator()(Octree *p); };
using OctreePtr = std::unique_ptr<Octree, OctreeDeleter>;
struct Octree
{
std::unique_ptr<Cube> root_cube;
Vec3d origin;
std::vector<CubeProperties> cubes_properties;
// Calculate line spacing for
// 1) adaptive cubic infill
// 2) adaptive internal support cubic infill
// Returns zero for a particular infill type if no such infill is to be generated.
std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_object);
Octree(std::unique_ptr<Cube> rootCube, const Vec3d &origin, const std::vector<CubeProperties> &cubes_properties)
: root_cube(std::move(rootCube)), origin(origin), cubes_properties(cubes_properties) {}
// Rotation of the octree to stand on one of its corners.
Eigen::Quaterniond transform_to_world();
// Inverse roation of the above.
Eigen::Quaterniond transform_to_octree();
inline static int find_octant(const Vec3d &i_cube, const Vec3d &current)
{
return (i_cube.z() > current.z()) * 4 + (i_cube.y() > current.y()) * 2 + (i_cube.x() > current.x());
}
static void propagate_point(
Vec3d point,
FillAdaptive_Internal::Cube *current_cube,
int depth,
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties);
};
}; // namespace FillAdaptive_Internal
FillAdaptive::OctreePtr build_octree(
// Mesh is rotated to the coordinate system of the octree.
const indexed_triangle_set &triangle_mesh,
// Overhang triangles extracted from fill surfaces with stInternalBridge type,
// rotated to the coordinate system of the octree.
const std::vector<Vec3d> &overhang_triangles,
coordf_t line_spacing,
// If true, octree is densified below internal overhangs only.
bool support_overhangs_only);
//
// Some of the algorithms used by class FillAdaptive were inspired by
// Cura Engine's class SubDivCube
// https://github.com/Ultimaker/CuraEngine/blob/master/src/infill/SubDivCube.h
//
class FillAdaptive : public Fill
class Filler : public Slic3r::Fill
{
public:
virtual ~FillAdaptive() {}
virtual ~Filler() {}
protected:
virtual Fill* clone() const { return new FillAdaptive(*this); };
virtual Fill* clone() const { return new Filler(*this); };
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines &polylines_out);
virtual bool no_sort() const { return true; }
void generate_infill_lines(
FillAdaptive_Internal::Cube *cube,
double z_position,
const Vec3d & origin,
const Transform3d & rotation_matrix,
std::vector<Lines> & dir_lines_out,
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties,
int depth);
static void connect_lines(Lines &lines, Line new_line);
void generate_infill(const FillParams & params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon & expolygon,
Polylines & polylines_out,
FillAdaptive_Internal::Octree *octree);
public:
static std::unique_ptr<FillAdaptive_Internal::Octree> build_octree(
TriangleMesh &triangle_mesh,
coordf_t line_spacing,
const Vec3d & cube_center);
static void expand_cube(
FillAdaptive_Internal::Cube *cube,
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties,
const AABBTreeIndirect::Tree3f &distance_tree,
const TriangleMesh & triangle_mesh,
int depth);
};
class FillSupportCubic : public FillAdaptive
{
public:
virtual ~FillSupportCubic() = default;
protected:
virtual Fill* clone() const { return new FillSupportCubic(*this); };
virtual bool no_sort() const { return true; }
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines &polylines_out);
public:
static std::unique_ptr<FillAdaptive_Internal::Octree> build_octree(
TriangleMesh & triangle_mesh,
coordf_t line_spacing,
const Vec3d & cube_center,
const Transform3d &rotation_matrix);
};
// Calculate line spacing for
// 1) adaptive cubic infill
// 2) adaptive internal support cubic infill
// Returns zero for a particular infill type if no such infill is to be generated.
std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_object);
}; // namespace FillAdaptive
} // namespace Slic3r
#endif // slic3r_FillAdaptive_hpp_

View file

@ -38,9 +38,9 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipArchimedeanChords: return new FillArchimedeanChords();
case ipHilbertCurve: return new FillHilbertCurve();
case ipOctagramSpiral: return new FillOctagramSpiral();
case ipAdaptiveCubic: return new FillAdaptive();
case ipSupportCubic: return new FillSupportCubic();
default: throw std::invalid_argument("unknown type");
case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler();
default: throw Slic3r::InvalidArgument("unknown type");
}
}
@ -847,8 +847,9 @@ void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_
boundary.assign(boundary_src.holes.size() + 1, Points());
boundary_data.assign(boundary_src.holes.size() + 1, std::vector<ContourPointData>());
// Mapping the infill_ordered end point to a (contour, point) of boundary.
std::vector<std::pair<size_t, size_t>> map_infill_end_point_to_boundary;
map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, std::pair<size_t, size_t>(std::numeric_limits<size_t>::max(), std::numeric_limits<size_t>::max()));
std::vector<std::pair<size_t, size_t>> map_infill_end_point_to_boundary;
static constexpr auto boundary_idx_unconnected = std::numeric_limits<size_t>::max();
map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, std::pair<size_t, size_t>(boundary_idx_unconnected, boundary_idx_unconnected));
{
// Project the infill_ordered end points onto boundary_src.
std::vector<std::pair<EdgeGrid::Grid::ClosestPointResult, size_t>> intersection_points;
@ -898,13 +899,14 @@ void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_
contour_data.front().param = contour_data.back().param + (contour_dst.back().cast<float>() - contour_dst.front().cast<float>()).norm();
}
#ifndef NDEBUG
assert(boundary.size() == boundary_src.num_contours());
assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(),
#if 0
// Adaptive Cubic Infill produces infill lines, which not always end at the outer boundary.
assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(),
[&boundary](const std::pair<size_t, size_t> &contour_point) {
return contour_point.first < boundary.size() && contour_point.second < boundary[contour_point.first].size();
}));
#endif /* NDEBUG */
#endif
}
// Mark the points and segments of split boundary as consumed if they are very close to some of the infill line.
@ -935,9 +937,9 @@ void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_
const Polyline &pl2 = infill_ordered[idx_chain];
const std::pair<size_t, size_t> *cp1 = &map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1];
const std::pair<size_t, size_t> *cp2 = &map_infill_end_point_to_boundary[idx_chain * 2];
const std::vector<ContourPointData> &contour_data = boundary_data[cp1->first];
if (cp1->first == cp2->first) {
if (cp1->first != boundary_idx_unconnected && cp1->first == cp2->first) {
// End points on the same contour. Try to connect them.
const std::vector<ContourPointData> &contour_data = boundary_data[cp1->first];
float param_lo = (cp1->second == 0) ? 0.f : contour_data[cp1->second].param;
float param_hi = (cp2->second == 0) ? 0.f : contour_data[cp2->second].param;
float param_end = contour_data.front().param;
@ -964,7 +966,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_
const std::pair<size_t, size_t> *cp1prev = cp1 - 1;
const std::pair<size_t, size_t> *cp2 = &map_infill_end_point_to_boundary[(connection_cost.idx_first + 1) * 2];
const std::pair<size_t, size_t> *cp2next = cp2 + 1;
assert(cp1->first == cp2->first);
assert(cp1->first == cp2->first && cp1->first != boundary_idx_unconnected);
std::vector<ContourPointData> &contour_data = boundary_data[cp1->first];
if (connection_cost.reversed)
std::swap(cp1, cp2);

View file

@ -11,6 +11,7 @@
#include "../libslic3r.h"
#include "../BoundingBox.hpp"
#include "../Exception.hpp"
#include "../Utils.hpp"
namespace Slic3r {
@ -19,13 +20,14 @@ class ExPolygon;
class Surface;
enum InfillPattern : int;
namespace FillAdaptive_Internal {
namespace FillAdaptive {
struct Octree;
};
class InfillFailedException : public std::runtime_error {
// Infill shall never fail, therefore the error is classified as RuntimeError, not SlicingError.
class InfillFailedException : public Slic3r::RuntimeError {
public:
InfillFailedException() : std::runtime_error("Infill failed") {}
InfillFailedException() : Slic3r::RuntimeError("Infill failed") {}
};
struct FillParams
@ -74,9 +76,7 @@ public:
BoundingBox bounding_box;
// Octree builds on mesh for usage in the adaptive cubic infill
FillAdaptive_Internal::Octree* adapt_fill_octree = nullptr;
// Octree builds on mesh for usage in the support cubic infill
FillAdaptive_Internal::Octree* support_fill_octree = nullptr;
FillAdaptive::Octree* adapt_fill_octree = nullptr;
public:
virtual ~Fill() {}

View file

@ -53,7 +53,7 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key)
else if (opt_key == "support_material_extrusion_width")
return frSupportMaterial;
else
throw std::runtime_error("opt_key_to_flow_role: invalid argument");
throw Slic3r::RuntimeError("opt_key_to_flow_role: invalid argument");
};
static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key)
@ -126,7 +126,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
{
// we need layer height unless it's a bridge
if (height <= 0 && bridge_flow_ratio == 0)
throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()");
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
float w;
if (bridge_flow_ratio > 0) {
@ -151,7 +151,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height,
{
// we need layer height unless it's a bridge
if (height <= 0 && !bridge)
throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()");
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()");
// Calculate width from spacing.
// For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions.
// For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads.

View file

@ -3,6 +3,7 @@
#include "libslic3r.h"
#include "Config.hpp"
#include "Exception.hpp"
#include "ExtrusionEntity.hpp"
namespace Slic3r {
@ -27,11 +28,11 @@ enum FlowRole {
frSupportMaterialInterface,
};
class FlowError : public std::invalid_argument
class FlowError : public Slic3r::InvalidArgument
{
public:
FlowError(const std::string& what_arg) : invalid_argument(what_arg) {}
FlowError(const char* what_arg) : invalid_argument(what_arg) {}
FlowError(const std::string& what_arg) : Slic3r::InvalidArgument(what_arg) {}
FlowError(const char* what_arg) : Slic3r::InvalidArgument(what_arg) {}
};
class FlowErrorNegativeSpacing : public FlowError

View file

@ -1,4 +1,5 @@
#include "../libslic3r.h"
#include "../Exception.hpp"
#include "../Model.hpp"
#include "../Utils.hpp"
#include "../GCode.hpp"
@ -123,11 +124,11 @@ const char* INVALID_OBJECT_TYPES[] =
"other"
};
class version_error : public std::runtime_error
class version_error : public Slic3r::FileIOError
{
public:
version_error(const std::string& what_arg) : std::runtime_error(what_arg) {}
version_error(const char* what_arg) : std::runtime_error(what_arg) {}
version_error(const std::string& what_arg) : Slic3r::FileIOError(what_arg) {}
version_error(const char* what_arg) : Slic3r::FileIOError(what_arg) {}
};
const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key)
@ -607,7 +608,7 @@ namespace Slic3r {
{
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
throw std::runtime_error(e.what());
throw Slic3r::FileIOError(e.what());
}
}
}
@ -780,7 +781,7 @@ namespace Slic3r {
{
char error_buf[1024];
::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
throw std::runtime_error(error_buf);
throw Slic3r::FileIOError(error_buf);
}
return n;
@ -789,7 +790,7 @@ namespace Slic3r {
catch (const version_error& e)
{
// rethrow the exception
throw std::runtime_error(e.what());
throw Slic3r::FileIOError(e.what());
}
catch (std::exception& e)
{
@ -2360,9 +2361,9 @@ namespace Slic3r {
continue;
if (!volume->mesh().repaired)
throw std::runtime_error("store_3mf() requires repair()");
throw Slic3r::FileIOError("store_3mf() requires repair()");
if (!volume->mesh().has_shared_vertices())
throw std::runtime_error("store_3mf() requires shared vertices");
throw Slic3r::FileIOError("store_3mf() requires shared vertices");
volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first;

View file

@ -7,6 +7,7 @@
#include <boost/nowide/cstdio.hpp>
#include "../libslic3r.h"
#include "../Exception.hpp"
#include "../Model.hpp"
#include "../GCode.hpp"
#include "../PrintConfig.hpp"
@ -923,7 +924,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
{
char error_buf[1024];
::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
throw std::runtime_error(error_buf);
throw Slic3r::FileIOError(error_buf);
}
return n;
@ -948,9 +949,9 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
if (check_version && (ctx.m_version > VERSION_AMF_COMPATIBLE))
{
// std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."));
// throw std::runtime_error(msg.c_str());
// throw Slic3r::FileIOError(msg.c_str());
const std::string msg = (boost::format(_(L("The selected amf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str();
throw std::runtime_error(msg);
throw Slic3r::FileIOError(msg);
}
return true;
@ -994,7 +995,7 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
{
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
throw std::runtime_error(e.what());
throw Slic3r::FileIOError(e.what());
}
break;
@ -1147,9 +1148,9 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
if (! volume->mesh().repaired)
throw std::runtime_error("store_amf() requires repair()");
throw Slic3r::FileIOError("store_amf() requires repair()");
if (! volume->mesh().has_shared_vertices())
throw std::runtime_error("store_amf() requires shared vertices");
throw Slic3r::FileIOError("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {

View file

@ -147,7 +147,7 @@ static void extract_model_from_archive(
}
}
if (! trafo_set)
throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
// Extract the STL.
StlHeader header;
@ -266,7 +266,7 @@ static void extract_model_from_archive(
}
if (! mesh_valid)
throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
// Add this mesh to the model.
ModelVolume *volume = nullptr;
@ -303,7 +303,7 @@ bool load_prus(const char *path, Model *model)
mz_bool res = MZ_FALSE;
try {
if (!open_zip_reader(&archive, path))
throw std::runtime_error(std::string("Unable to init zip reader for ") + path);
throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path);
std::vector<char> scene_xml_data;
// For grouping multiple STLs into a single ModelObject for multi-material prints.
std::map<int, ModelObject*> group_to_model_object;
@ -316,10 +316,10 @@ bool load_prus(const char *path, Model *model)
buffer.assign((size_t)stat.m_uncomp_size, 0);
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == MZ_FALSE)
std::runtime_error(std::string("Error while extracting a file from ") + path);
throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
if (strcmp(stat.m_filename, "scene.xml") == 0) {
if (! scene_xml_data.empty())
throw std::runtime_error(std::string("Multiple scene.xml were found in the archive.") + path);
throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path);
scene_xml_data = std::move(buffer);
} else if (boost::iends_with(stat.m_filename, ".stl")) {
// May throw std::exception

View file

@ -10,6 +10,7 @@
#include <sstream>
#include "libslic3r/Exception.hpp"
#include "libslic3r/SlicesToTriangleMesh.hpp"
#include "libslic3r/MarchingSquares.hpp"
#include "libslic3r/ClipperUtils.hpp"
@ -64,7 +65,7 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry,
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
buf.data(), buf.size(), 0))
throw std::runtime_error(zip.get_errorstr());
throw Slic3r::FileIOError(zip.get_errorstr());
boost::property_tree::ptree tree;
std::stringstream ss(buf);
@ -80,7 +81,7 @@ PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
buf.data(), buf.size(), 0))
throw std::runtime_error(zip.get_errorstr());
throw Slic3r::FileIOError(zip.get_errorstr());
return {std::move(buf), (name.empty() ? entry.m_filename : name)};
}
@ -94,7 +95,7 @@ ArchiveData extract_sla_archive(const std::string &zipfname,
struct Arch: public MZ_Archive {
Arch(const std::string &fname) {
if (!open_zip_reader(&arch, fname))
throw std::runtime_error(get_errorstr());
throw Slic3r::FileIOError(get_errorstr());
}
~Arch() { close_zip_reader(&arch); }
@ -202,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
throw std::runtime_error("Invalid SL1 file");
throw Slic3r::FileIOError("Invalid SL1 file");
RasterParams rstp;
@ -228,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
if (!opt_layerh || !opt_init_layerh)
throw std::runtime_error("Invalid SL1 file");
throw Slic3r::FileIOError("Invalid SL1 file");
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
}

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,7 @@
#include "GCode/SpiralVase.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
#include "GCode/SeamPlacer.hpp"
#if ENABLE_GCODE_VIEWER
#include "GCode/GCodeProcessor.hpp"
#else
@ -69,6 +70,7 @@ private:
std::unique_ptr<MotionPlanner> m_layer_mp;
};
class OozePrevention {
public:
bool enable;
@ -338,6 +340,9 @@ private:
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z);
// Cache for custom seam enforcers/blockers for each layer.
SeamPlacer m_seam_placer;
/* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to()
methods. */
@ -376,7 +381,6 @@ private:
// Current layer processed. Insequential printing mode, only a single copy will be printed.
// In non-sequential mode, all its copies will be printed.
const Layer* m_layer;
std::map<const PrintObject*,Point> m_seam_position;
double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active?
ExtrusionRole m_last_extrusion_role;

View file

@ -319,13 +319,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
{
boost::nowide::ifstream in(filename);
if (!in.good())
throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
// temporary file to contain modified gcode
std::string out_path = filename + ".postprocess";
FILE* out = boost::nowide::fopen(out_path.c_str(), "wb");
if (out == nullptr)
throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
auto time_in_minutes = [](float time_in_seconds) {
return int(::roundf(time_in_seconds / 60.0f));
@ -418,7 +418,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
in.close();
fclose(out);
boost::nowide::remove(out_path.c_str());
throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
}
export_line.clear();
};
@ -426,7 +426,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
while (std::getline(in, gcode_line)) {
if (!in.good()) {
fclose(out);
throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
}
gcode_line += "\n";
@ -460,7 +460,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
in.close();
if (rename_file(out_path, filename))
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
"Is " + out_path + " locked?" + '\n');
}

View file

@ -79,7 +79,7 @@ static DWORD execute_process_winapi(const std::wstring &command_line)
if (! ::CreateProcessW(
nullptr /* lpApplicationName */, (LPWSTR)command_line.c_str(), nullptr /* lpProcessAttributes */, nullptr /* lpThreadAttributes */, false /* bInheritHandles */,
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID)envstr.c_str(), nullptr /* lpCurrentDirectory */, &startup_info, &process_info))
throw std::runtime_error(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
throw Slic3r::RuntimeError(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
::WaitForSingleObject(process_info.hProcess, INFINITE);
ULONG rc = 0;
::GetExitCodeProcess(process_info.hProcess, &rc);
@ -98,13 +98,13 @@ static int run_script(const std::string &script, const std::string &gcode, std::
LPWSTR *szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs);
if (szArglist == nullptr || nArgs <= 0) {
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
throw std::runtime_error(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
throw Slic3r::RuntimeError(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
}
std::wstring command_line;
std::wstring command = szArglist[0];
if (! boost::filesystem::exists(boost::filesystem::path(command)))
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
throw Slic3r::RuntimeError(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
if (boost::iends_with(command, L".pl")) {
// This is a perl script. Run it through the perl interpreter.
// The current process may be slic3r.exe or slic3r-console.exe.
@ -115,7 +115,7 @@ static int run_script(const std::string &script, const std::string &gcode, std::
boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe";
if (! boost::filesystem::exists(path_perl)) {
LocalFree(szArglist);
throw std::runtime_error(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
throw Slic3r::RuntimeError(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
}
// Replace it with the current perl interpreter.
quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line);
@ -187,7 +187,7 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
config.setenv_();
auto gcode_file = boost::filesystem::path(path);
if (! boost::filesystem::exists(gcode_file))
throw std::runtime_error(std::string("Post-processor can't find exported gcode file"));
throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file"));
for (const std::string &scripts : config.post_process.values) {
std::vector<std::string> lines;
@ -205,7 +205,7 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str()
: (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str();
BOOST_LOG_TRIVIAL(error) << msg;
throw std::runtime_error(msg);
throw Slic3r::RuntimeError(msg);
}
}
}

View file

@ -148,7 +148,7 @@ static inline int parse_int(const char *&line)
char *endptr = NULL;
long result = strtol(line, &endptr, 10);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw std::runtime_error("PressureEqualizer: Error parsing an int");
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing an int");
line = endptr;
return int(result);
};
@ -160,7 +160,7 @@ static inline float parse_float(const char *&line)
char *endptr = NULL;
float result = strtof(line, &endptr);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw std::runtime_error("PressureEqualizer: Error parsing a float");
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
line = endptr;
return result;
};
@ -229,7 +229,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
assert(false);
}
if (i == -1)
throw std::runtime_error(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
buf.pos_provided[i] = true;
new_pos[i] = parse_float(line);
if (i == 3 && m_config->use_relative_e_distances.value)
@ -298,7 +298,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
set = true;
break;
default:
throw std::runtime_error(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
}
eatws(line);
}

View file

@ -94,7 +94,7 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
if (extrusion_entity_collection != nullptr)
return extrusionentity_extents(*extrusion_entity_collection);
throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()");
throw Slic3r::RuntimeError("Unexpected extrusion_entity type in extrusionentity_extents()");
return BoundingBoxf();
}

View file

@ -0,0 +1,672 @@
#include "SeamPlacer.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/EdgeGrid.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/SVG.hpp"
namespace Slic3r {
// This penalty is added to all points inside custom blockers (subtracted from pts inside enforcers).
static constexpr float ENFORCER_BLOCKER_PENALTY = 100;
// In case there are custom enforcers/blockers, the loop polygon shall always have
// sides smaller than this (so it isn't limited to original resolution).
static constexpr float MINIMAL_POLYGON_SIDE = scale_(0.2f);
// When spAligned is active and there is a support enforcer,
// add this penalty to its center.
static constexpr float ENFORCER_CENTER_PENALTY = -10.f;
static float extrudate_overlap_penalty(float nozzle_r, float weight_zero, float overlap_distance)
{
// The extrudate is not fully supported by the lower layer. Fit a polynomial penalty curve.
// Solved by sympy package:
/*
from sympy import *
(x,a,b,c,d,r,z)=symbols('x a b c d r z')
p = a + b*x + c*x*x + d*x*x*x
p2 = p.subs(solve([p.subs(x, -r), p.diff(x).subs(x, -r), p.diff(x,x).subs(x, -r), p.subs(x, 0)-z], [a, b, c, d]))
from sympy.plotting import plot
plot(p2.subs(r,0.2).subs(z,1.), (x, -1, 3), adaptive=False, nb_of_points=400)
*/
if (overlap_distance < - nozzle_r) {
// The extrudate is fully supported by the lower layer. This is the ideal case, therefore zero penalty.
return 0.f;
} else {
float x = overlap_distance / nozzle_r;
float x2 = x * x;
float x3 = x2 * x;
return weight_zero * (1.f + 3.f * x + 3.f * x2 + x3);
}
}
// Return a value in <0, 1> of a cubic B-spline kernel centered around zero.
// The B-spline is re-scaled so it has value 1 at zero.
static inline float bspline_kernel(float x)
{
x = std::abs(x);
if (x < 1.f) {
return 1.f - (3.f / 2.f) * x * x + (3.f / 4.f) * x * x * x;
}
else if (x < 2.f) {
x -= 1.f;
float x2 = x * x;
float x3 = x2 * x;
return (1.f / 4.f) - (3.f / 4.f) * x + (3.f / 4.f) * x2 - (1.f / 4.f) * x3;
}
else
return 0;
}
static Points::const_iterator project_point_to_polygon_and_insert(Polygon &polygon, const Point &pt, double eps)
{
assert(polygon.points.size() >= 2);
if (polygon.points.size() <= 1)
if (polygon.points.size() == 1)
return polygon.points.begin();
Point pt_min;
double d_min = std::numeric_limits<double>::max();
size_t i_min = size_t(-1);
for (size_t i = 0; i < polygon.points.size(); ++ i) {
size_t j = i + 1;
if (j == polygon.points.size())
j = 0;
const Point &p1 = polygon.points[i];
const Point &p2 = polygon.points[j];
const Slic3r::Point v_seg = p2 - p1;
const Slic3r::Point v_pt = pt - p1;
const int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1));
int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1));
if (t_pt < 0) {
// Closest to p1.
double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1)));
if (dabs < d_min) {
d_min = dabs;
i_min = i;
pt_min = p1;
}
}
else if (t_pt > l2_seg) {
// Closest to p2. Then p2 is the starting point of another segment, which shall be discovered in the next step.
continue;
} else {
// Closest to the segment.
assert(t_pt >= 0 && t_pt <= l2_seg);
int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1));
double d = double(d_seg) / sqrt(double(l2_seg));
double dabs = std::abs(d);
if (dabs < d_min) {
d_min = dabs;
i_min = i;
// Evaluate the foot point.
pt_min = p1;
double linv = double(d_seg) / double(l2_seg);
pt_min(0) = pt(0) - coord_t(floor(double(v_seg(1)) * linv + 0.5));
pt_min(1) = pt(1) + coord_t(floor(double(v_seg(0)) * linv + 0.5));
assert(Line(p1, p2).distance_to(pt_min) < scale_(1e-5));
}
}
}
assert(i_min != size_t(-1));
if ((pt_min - polygon.points[i_min]).cast<double>().norm() > eps) {
// Insert a new point on the segment i_min, i_min+1.
return polygon.points.insert(polygon.points.begin() + (i_min + 1), pt_min);
}
return polygon.points.begin() + i_min;
}
static std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths, float min_arm_length)
{
assert(polygon.points.size() + 1 == lengths.size());
if (min_arm_length > 0.25f * lengths.back())
min_arm_length = 0.25f * lengths.back();
// Find the initial prev / next point span.
size_t idx_prev = polygon.points.size();
size_t idx_curr = 0;
size_t idx_next = 1;
while (idx_prev > idx_curr && lengths.back() - lengths[idx_prev] < min_arm_length)
-- idx_prev;
while (idx_next < idx_prev && lengths[idx_next] < min_arm_length)
++ idx_next;
std::vector<float> angles(polygon.points.size(), 0.f);
for (; idx_curr < polygon.points.size(); ++ idx_curr) {
// Move idx_prev up until the distance between idx_prev and idx_curr is lower than min_arm_length.
if (idx_prev >= idx_curr) {
while (idx_prev < polygon.points.size() && lengths.back() - lengths[idx_prev] + lengths[idx_curr] > min_arm_length)
++ idx_prev;
if (idx_prev == polygon.points.size())
idx_prev = 0;
}
while (idx_prev < idx_curr && lengths[idx_curr] - lengths[idx_prev] > min_arm_length)
++ idx_prev;
// Move idx_prev one step back.
if (idx_prev == 0)
idx_prev = polygon.points.size() - 1;
else
-- idx_prev;
// Move idx_next up until the distance between idx_curr and idx_next is greater than min_arm_length.
if (idx_curr <= idx_next) {
while (idx_next < polygon.points.size() && lengths[idx_next] - lengths[idx_curr] < min_arm_length)
++ idx_next;
if (idx_next == polygon.points.size())
idx_next = 0;
}
while (idx_next < idx_curr && lengths.back() - lengths[idx_curr] + lengths[idx_next] < min_arm_length)
++ idx_next;
// Calculate angle between idx_prev, idx_curr, idx_next.
const Point &p0 = polygon.points[idx_prev];
const Point &p1 = polygon.points[idx_curr];
const Point &p2 = polygon.points[idx_next];
const Point v1 = p1 - p0;
const Point v2 = p2 - p1;
int64_t dot = int64_t(v1(0))*int64_t(v2(0)) + int64_t(v1(1))*int64_t(v2(1));
int64_t cross = int64_t(v1(0))*int64_t(v2(1)) - int64_t(v1(1))*int64_t(v2(0));
float angle = float(atan2(double(cross), double(dot)));
angles[idx_curr] = angle;
}
return angles;
}
void SeamPlacer::init(const Print& print)
{
m_enforcers.clear();
m_blockers.clear();
//m_last_seam_position.clear();
m_seam_history.clear();
for (const PrintObject* po : print.objects()) {
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, m_enforcers);
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, m_blockers);
}
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
for (ExPolygons& explgs : m_enforcers)
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
for (ExPolygons& explgs : m_blockers)
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
}
Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_position,
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
{
Polygon polygon = loop.polygon();
BoundingBox polygon_bb = polygon.bounding_box();
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
if (this->is_custom_seam_on_layer(layer_idx)) {
// Seam enf/blockers can begin and end in between the original vertices.
// Let add extra points in between and update the leghths.
polygon.densify(MINIMAL_POLYGON_SIDE);
}
if (seam_position != spRandom) {
// Retrieve the last start position for this object.
float last_pos_weight = 1.f;
if (seam_position == spAligned) {
// Seam is aligned to the seam at the preceding layer.
if (po != nullptr) {
std::optional<Point> pos = m_seam_history.get_last_seam(po, layer_idx, polygon_bb);
if (pos.has_value()) {
//last_pos = m_last_seam_position[po];
last_pos = *pos;
last_pos_weight = is_custom_enforcer_on_layer(layer_idx) ? 0.f : 1.f;
}
}
}
else if (seam_position == spRear) {
// Object is centered around (0,0) in its current coordinate system.
last_pos.x() = 0;
last_pos.y() += coord_t(3. * po->bounding_box().radius());
last_pos_weight = 5.f;
} if (seam_position == spNearest) {
// last_pos already contains current nozzle position
}
// Insert a projection of last_pos into the polygon.
size_t last_pos_proj_idx;
{
auto it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r);
last_pos_proj_idx = it - polygon.points.begin();
}
// Parametrize the polygon by its length.
std::vector<float> lengths = polygon.parameter_by_length();
// For each polygon point, store a penalty.
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, float(nozzle_r));
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
const float penaltyConvexVertex = 1.f;
const float penaltyFlatSurface = 5.f;
const float penaltyOverhangHalf = 10.f;
// Penalty for visible seams.
for (size_t i = 0; i < polygon.points.size(); ++ i) {
float ccwAngle = penalties[i];
if (was_clockwise)
ccwAngle = - ccwAngle;
float penalty = 0;
if (ccwAngle <- float(0.6 * PI))
// Sharp reflex vertex. We love that, it hides the seam perfectly.
penalty = 0.f;
else if (ccwAngle > float(0.6 * PI))
// Seams on sharp convex vertices are more visible than on reflex vertices.
penalty = penaltyConvexVertex;
else if (ccwAngle < 0.f) {
// Interpolate penalty between maximum and zero.
penalty = penaltyFlatSurface * bspline_kernel(ccwAngle * float(PI * 2. / 3.));
} else {
assert(ccwAngle >= 0.f);
// Interpolate penalty between maximum and the penalty for a convex vertex.
penalty = penaltyConvexVertex + (penaltyFlatSurface - penaltyConvexVertex) * bspline_kernel(ccwAngle * float(PI * 2. / 3.));
}
// Give a negative penalty for points close to the last point or the prefered seam location.
float dist_to_last_pos_proj = (i < last_pos_proj_idx) ?
std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) :
std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]);
float dist_max = 0.1f * lengths.back(); // 5.f * nozzle_dmr
penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max);
penalties[i] = std::max(0.f, penalty);
}
// Penalty for overhangs.
if (lower_layer_edge_grid) {
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
coord_t nozzle_r = coord_t(std::floor(scale_(0.5 * nozzle_dmr) + 0.5));
coord_t search_r = coord_t(std::floor(scale_(0.8 * nozzle_dmr) + 0.5));
for (size_t i = 0; i < polygon.points.size(); ++ i) {
const Point &p = polygon.points[i];
coordf_t dist;
// Signed distance is positive outside the object, negative inside the object.
// The point is considered at an overhang, if it is more than nozzle radius
// outside of the lower layer contour.
[[maybe_unused]] bool found = lower_layer_edge_grid->signed_distance(p, search_r, dist);
// If the approximate Signed Distance Field was initialized over lower_layer_edge_grid,
// then the signed distnace shall always be known.
assert(found);
penalties[i] += extrudate_overlap_penalty(float(nozzle_r), penaltyOverhangHalf, float(dist));
}
}
// Custom seam. Huge (negative) constant penalty is applied inside
// blockers (enforcers) to rule out points that should not win.
this->apply_custom_seam(polygon, penalties, lengths, layer_idx, seam_position);
// Find a point with a minimum penalty.
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx)) {
// Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx.
// In that case use last_pos_proj_idx instead.
float penalty_aligned = penalties[last_pos_proj_idx];
float penalty_min = penalties[idx_min];
float penalty_diff_abs = std::abs(penalty_min - penalty_aligned);
float penalty_max = std::max(penalty_min, penalty_aligned);
float penalty_diff_rel = (penalty_max == 0.f) ? 0.f : penalty_diff_abs / penalty_max;
// printf("Align seams, penalty aligned: %f, min: %f, diff abs: %f, diff rel: %f\n", penalty_aligned, penalty_min, penalty_diff_abs, penalty_diff_rel);
if (std::abs(penalty_diff_rel) < 0.05) {
// Penalty of the aligned point is very close to the minimum penalty.
// Align the seams as accurately as possible.
idx_min = last_pos_proj_idx;
}
}
if (seam_position == spAligned && loop.role() == erExternalPerimeter)
m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb);
// Export the contour into a SVG file.
#if 0
{
static int iRun = 0;
SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++));
if (m_layer->lower_layer != NULL)
svg.draw(m_layer->lower_layer->slices);
for (size_t i = 0; i < loop.paths.size(); ++ i)
svg.draw(loop.paths[i].as_polyline(), "red");
Polylines polylines;
for (size_t i = 0; i < loop.paths.size(); ++ i)
polylines.push_back(loop.paths[i].as_polyline());
Slic3r::Polygons polygons;
coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
coord_t delta = scale_(0.5*nozzle_dmr);
Slic3r::offset(polylines, &polygons, delta);
// for (size_t i = 0; i < polygons.size(); ++ i) svg.draw((Polyline)polygons[i], "blue");
svg.draw(last_pos, "green", 3);
svg.draw(polygon.points[idx_min], "yellow", 3);
svg.Close();
}
#endif
return polygon.points[idx_min];
} else { // spRandom
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
// This loop does not contain any other loop. Set a random position.
// The other loops will get a seam close to the random point chosen
// on the innermost contour.
//FIXME This works correctly for inner contours first only.
last_pos = this->get_random_seam(layer_idx, polygon);
}
if (loop.role() == erExternalPerimeter && is_custom_seam_on_layer(layer_idx)) {
// There is a possibility that the loop will be influenced by custom
// seam enforcer/blocker. In this case do not inherit the seam
// from internal loops (which may conflict with the custom selection
// and generate another random one.
bool saw_custom = false;
Point candidate = this->get_random_seam(layer_idx, polygon, &saw_custom);
if (saw_custom)
last_pos = candidate;
}
return last_pos;
}
}
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
bool* saw_custom) const
{
// Parametrize the polygon by its length.
std::vector<float> lengths = polygon.parameter_by_length();
// Which of the points are inside enforcers/blockers?
std::vector<size_t> enforcers_idxs;
std::vector<size_t> blockers_idxs;
this->get_enforcers_and_blockers(layer_idx, polygon, enforcers_idxs, blockers_idxs);
bool has_enforcers = ! enforcers_idxs.empty();
bool has_blockers = ! blockers_idxs.empty();
if (saw_custom)
*saw_custom = has_enforcers || has_blockers;
// FIXME FIXME FIXME: This is just to test the outcome and whether it is
// reasonable. The algorithm should really sum the length of all available
// pieces, get a random length and find the respective point.
float rand_len = 0.f;
size_t pt_idx = 0;
do {
rand_len = lengths.back() * (rand()/float(RAND_MAX));
auto it = std::lower_bound(lengths.begin(), lengths.end(), rand_len);
pt_idx = it == lengths.end() ? 0 : (it-lengths.begin()-1);
// If there are blockers and the point is inside, repeat.
// If there are enforcers and the point is NOT inside, repeat.
} while ((has_blockers && std::binary_search(blockers_idxs.begin(), blockers_idxs.end(), pt_idx))
|| (has_enforcers && ! std::binary_search(enforcers_idxs.begin(), enforcers_idxs.end(), pt_idx)));
if (! has_enforcers && ! has_blockers) {
// The polygon may be too coarse, calculate the point exactly.
bool last_seg = pt_idx == polygon.points.size()-1;
size_t next_idx = last_seg ? 0 : pt_idx+1;
const Point& prev = polygon.points[pt_idx];
const Point& next = polygon.points[next_idx];
assert(next_idx == 0 || pt_idx+1 == next_idx);
coordf_t diff_x = next.x() - prev.x();
coordf_t diff_y = next.y() - prev.y();
coordf_t dist = lengths[last_seg ? pt_idx+1 : next_idx] - lengths[pt_idx];
return Point(prev.x() + (rand_len - lengths[pt_idx]) * (diff_x/dist),
prev.y() + (rand_len - lengths[pt_idx]) * (diff_y/dist));
} else {
// The polygon should be dense enough.
return polygon.points[pt_idx];
}
}
void SeamPlacer::get_enforcers_and_blockers(size_t layer_id,
const Polygon& polygon,
std::vector<size_t>& enforcers_idxs,
std::vector<size_t>& blockers_idxs) const
{
enforcers_idxs.clear();
blockers_idxs.clear();
// FIXME: This is quadratic and it should be improved, maybe by building
// an AABB tree (or at least utilize bounding boxes).
for (size_t i=0; i<polygon.points.size(); ++i) {
if (! m_enforcers.empty()) {
assert(layer_id < m_enforcers.size());
for (const ExPolygon& explg : m_enforcers[layer_id]) {
if (explg.contains(polygon.points[i]))
enforcers_idxs.push_back(i);
}
}
if (! m_blockers.empty()) {
assert(layer_id < m_blockers.size());
for (const ExPolygon& explg : m_blockers[layer_id]) {
if (explg.contains(polygon.points[i]))
blockers_idxs.push_back(i);
}
}
}
}
// Go through the polygon, identify points inside support enforcers and return
// indices of points in the middle of each enforcer (measured along the contour).
static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
const std::vector<float>& lengths,
const std::vector<size_t>& enforcers_idxs)
{
std::vector<size_t> out;
assert(polygon.points.size()+1 == lengths.size());
assert(std::is_sorted(enforcers_idxs.begin(), enforcers_idxs.end()));
if (polygon.size() < 2 || enforcers_idxs.empty())
return out;
auto get_center_idx = [&polygon, &lengths](size_t start_idx, size_t end_idx) -> size_t {
assert(end_idx >= start_idx);
if (start_idx == end_idx)
return start_idx;
float t_c = lengths[start_idx] + 0.5f * (lengths[end_idx] - lengths[start_idx]);
auto it = std::lower_bound(lengths.begin() + start_idx, lengths.begin() + end_idx, t_c);
int ret = it - lengths.begin();
return ret;
};
int last_enforcer_start_idx = enforcers_idxs.front();
bool first_pt_in_list = enforcers_idxs.front() != 0;
bool last_pt_in_list = enforcers_idxs.back() == polygon.points.size() - 1;
bool wrap_around = last_pt_in_list && first_pt_in_list;
for (size_t i=0; i<enforcers_idxs.size(); ++i) {
if (i != enforcers_idxs.size() - 1) {
if (enforcers_idxs[i+1] != enforcers_idxs[i] + 1) {
// i is last point of current enforcer
out.push_back(get_center_idx(last_enforcer_start_idx, enforcers_idxs[i]));
last_enforcer_start_idx = enforcers_idxs[i+1];
}
} else {
if (! wrap_around) {
// we can safely use the last enforcer point.
out.push_back(get_center_idx(last_enforcer_start_idx, enforcers_idxs[i]));
}
}
}
if (wrap_around) {
// Update first center already found.
if (out.empty()) {
// Probably an enforcer around the whole contour. Return nothing.
return out;
}
// find last point of the enforcer at the beginning:
size_t idx = 0;
while (enforcers_idxs[idx]+1 == enforcers_idxs[idx+1])
++idx;
float t_s = lengths[last_enforcer_start_idx];
float t_e = lengths[idx];
float half_dist = 0.5f * (t_e + lengths.back() - t_s);
float t_c = (half_dist > t_e) ? t_s + half_dist : t_e - half_dist;
auto it = std::lower_bound(lengths.begin(), lengths.end(), t_c);
out[0] = it - lengths.begin();
if (out[0] == lengths.size() - 1)
--out[0];
assert(out[0] < lengths.size() - 1);
}
return out;
}
void SeamPlacer::apply_custom_seam(const Polygon& polygon,
std::vector<float>& penalties,
const std::vector<float>& lengths,
int layer_id, SeamPosition seam_position) const
{
if (! is_custom_seam_on_layer(layer_id))
return;
std::vector<size_t> enforcers_idxs;
std::vector<size_t> blockers_idxs;
this->get_enforcers_and_blockers(layer_id, polygon, enforcers_idxs, blockers_idxs);
for (size_t i : enforcers_idxs) {
assert(i < penalties.size());
penalties[i] -= float(ENFORCER_BLOCKER_PENALTY);
}
for (size_t i : blockers_idxs) {
assert(i < penalties.size());
penalties[i] += float(ENFORCER_BLOCKER_PENALTY);
}
if (seam_position == spAligned) {
std::vector<size_t> enf_centers = find_enforcer_centers(polygon, lengths, enforcers_idxs);
for (size_t idx : enf_centers) {
assert(idx < penalties.size());
penalties[idx] += ENFORCER_CENTER_PENALTY;
}
}
////////////////////////
// std::ostringstream os;
// os << std::setw(3) << std::setfill('0') << layer_id;
// int a = scale_(20.);
// SVG svg("custom_seam" + os.str() + ".svg", BoundingBox(Point(-a, -a), Point(a, a)));
// /*if (! m_enforcers.empty())
// svg.draw(m_enforcers[layer_id], "blue");
// if (! m_blockers.empty())
// svg.draw(m_blockers[layer_id], "red");*/
// size_t min_idx = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
// //svg.draw(polygon.points[idx_min], "red", 6e5);
// for (size_t i=0; i<polygon.points.size(); ++i) {
// std::string fill;
// coord_t size = 0;
// if (min_idx == i) {
// fill = "yellow";
// size = 5e5;
// } else {
// fill = (std::find(enforcers_idxs.begin(), enforcers_idxs.end(), i) != enforcers_idxs.end() ? "green" : "black");
// if (std::find(enf_centers.begin(), enf_centers.end(), i) != enf_centers.end()) {
// size = 5e5;
// fill = "blue";
// }
// }
// if (i != 0)
// svg.draw(polygon.points[i], fill, size);
// else
// svg.draw(polygon.points[i], "red", 5e5);
// }
//////////////////////
}
std::optional<Point> SeamHistory::get_last_seam(const PrintObject* po, size_t layer_id, const BoundingBox& island_bb)
{
assert(layer_id >= m_layer_id);
if (layer_id > m_layer_id) {
// Get seam was called for different layer than last time.
m_data_last_layer = m_data_this_layer;
m_data_this_layer.clear();
m_layer_id = layer_id;
}
std::optional<Point> out;
auto seams_it = m_data_last_layer.find(po);
if (seams_it == m_data_last_layer.end())
return out;
const std::vector<SeamPoint>& seam_data_po = seams_it->second;
// Find a bounding-box on the last layer that is close to one we see now.
double min_score = std::numeric_limits<double>::max();
for (const SeamPoint& sp : seam_data_po) {
const BoundingBox& bb = sp.m_island_bb;
if (! bb.overlap(island_bb)) {
// This bb does not even overlap. It is likely unrelated.
continue;
}
double score = std::pow(bb.min(0) - island_bb.min(0), 2.)
+ std::pow(bb.min(1) - island_bb.min(1), 2.)
+ std::pow(bb.max(0) - island_bb.max(0), 2.)
+ std::pow(bb.max(1) - island_bb.max(1), 2.);
if (score < min_score) {
min_score = score;
out = sp.m_pos;
}
}
return out;
}
void SeamHistory::add_seam(const PrintObject* po, const Point& pos, const BoundingBox& island_bb)
{
m_data_this_layer[po].push_back({pos, island_bb});;
}
void SeamHistory::clear()
{
m_layer_id = 0;
m_data_last_layer.clear();
m_data_this_layer.clear();
}
}

View file

@ -0,0 +1,89 @@
#ifndef libslic3r_SeamPlacer_hpp_
#define libslic3r_SeamPlacer_hpp_
#include <optional>
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/BoundingBox.hpp"
namespace Slic3r {
class PrintObject;
class ExtrusionLoop;
class Print;
namespace EdgeGrid { class Grid; }
class SeamHistory {
public:
SeamHistory() { clear(); }
std::optional<Point> get_last_seam(const PrintObject* po, size_t layer_id, const BoundingBox& island_bb);
void add_seam(const PrintObject* po, const Point& pos, const BoundingBox& island_bb);
void clear();
private:
struct SeamPoint {
Point m_pos;
BoundingBox m_island_bb;
};
std::map<const PrintObject*, std::vector<SeamPoint>> m_data_last_layer;
std::map<const PrintObject*, std::vector<SeamPoint>> m_data_this_layer;
size_t m_layer_id;
};
class SeamPlacer {
public:
void init(const Print& print);
Point get_seam(const size_t layer_idx, const SeamPosition seam_position,
const ExtrusionLoop& loop, Point last_pos,
coordf_t nozzle_diameter, const PrintObject* po,
bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid);
private:
std::vector<ExPolygons> m_enforcers;
std::vector<ExPolygons> m_blockers;
//std::map<const PrintObject*, Point> m_last_seam_position;
SeamHistory m_seam_history;
// Get indices of points inside enforcers and blockers.
void get_enforcers_and_blockers(size_t layer_id,
const Polygon& polygon,
std::vector<size_t>& enforcers_idxs,
std::vector<size_t>& blockers_idxs) const;
// Apply penalties to points inside enforcers/blockers.
void apply_custom_seam(const Polygon& polygon,
std::vector<float>& penalties,
const std::vector<float>& lengths,
int layer_id, SeamPosition seam_position) const;
// Return random point of a polygon. The distribution will be uniform
// along the contour and account for enforcers and blockers.
Point get_random_seam(size_t layer_idx, const Polygon& polygon,
bool* saw_custom = nullptr) const;
// Is there any enforcer/blocker on this layer?
bool is_custom_seam_on_layer(size_t layer_id) const {
return is_custom_enforcer_on_layer(layer_id)
|| is_custom_blocker_on_layer(layer_id);
}
bool is_custom_enforcer_on_layer(size_t layer_id) const {
return (! m_enforcers.empty() && ! m_enforcers[layer_id].empty());
}
bool is_custom_blocker_on_layer(size_t layer_id) const {
return (! m_blockers.empty() && ! m_blockers[layer_id].empty());
}
};
}
#endif // libslic3r_SeamPlacer_hpp_

View file

@ -1,22 +1,6 @@
/*
TODO LIST
---------
1. cooling moves - DONE
2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
3. priming extrusions (last wipe must clear the color) - DONE
4. Peter's wipe tower - layer's are not exactly square
5. Peter's wipe tower - variable width for higher levels
6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
7. Peter's wipe tower - enable enhanced first layer adhesion
*/
#include "WipeTower.hpp"
#include <assert.h>
#include <math.h>
#include <cassert>
#include <iostream>
#include <vector>
#include <numeric>
@ -28,13 +12,16 @@ TODO LIST
#endif // ENABLE_GCODE_VIEWER
#include "BoundingBox.hpp"
#if defined(__linux) || defined(__GNUC__ )
#include <strings.h>
#endif /* __linux */
#ifdef _MSC_VER
#define strcasecmp _stricmp
#endif
// Experimental "Peter's wipe tower" feature was partially implemented, inspired by
// PJR's idea of alternating two perpendicular wiping directions on a square tower.
// It is probably never going to be finished, there are multiple remaining issues
// and there is probably no need to go down this way. m_peters_wipe_tower variable
// turns this on, maybe it should just be removed. Anyway, the issues are
// - layer's are not exactly square
// - variable width for higher levels
// - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
// - enable enhanced first layer adhesion
namespace Slic3r
@ -441,9 +428,26 @@ public:
WipeTowerWriter& append(const std::string& text) { m_gcode += text; return *this; }
std::vector<Vec2f> wipe_path() const
{
return m_wipe_path;
}
WipeTowerWriter& add_wipe_point(const Vec2f& pt)
{
m_wipe_path.push_back(rotate(pt));
return *this;
}
WipeTowerWriter& add_wipe_point(float x, float y)
{
return add_wipe_point(Vec2f(x, y));
}
private:
Vec2f m_start_pos;
Vec2f m_current_pos;
std::vector<Vec2f> m_wipe_path;
float m_current_z;
float m_current_feedrate;
size_t m_current_tool;
@ -713,7 +717,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
return results;
}
WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_layer)
WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
{
if ( m_print_brim )
return toolchange_Brim();
@ -790,7 +794,9 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
else {
writer.rectangle(Vec2f::Zero(), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
writer.add_wipe_point(writer.x(), writer.y())
.add_wipe_point(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
}
}
}
@ -820,6 +826,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
result.extrusions = writer.extrusions();
result.start_pos = writer.start_pos_rotated();
result.end_pos = writer.pos_rotated();
result.wipe_path = writer.wipe_path();
return result;
}
@ -853,13 +860,15 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
for (size_t i = 0; i < 4; ++ i) {
box.expand(spacing);
writer.travel (box.ld, 7000)
.extrude(box.lu, 2100).extrude(box.ru)
.extrude(box.rd ).extrude(box.ld);
.extrude(box.lu, 2100).extrude(box.ru)
.extrude(box.rd ).extrude(box.ld);
}
writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.
writer.travel(wipeTower_box.rd) // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
.travel(wipeTower_box.ld);
box.expand(-spacing);
writer.add_wipe_point(writer.x(), writer.y())
.add_wipe_point(box.ld)
.add_wipe_point(box.rd);
writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n");
@ -884,6 +893,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
result.extrusions = writer.extrusions();
result.start_pos = writer.start_pos_rotated();
result.end_pos = writer.pos_rotated();
result.wipe_path = writer.wipe_path();
return result;
}
@ -927,13 +937,6 @@ void WipeTower::toolchange_Unload(
else
sparse_beginning_y += (m_layer_info-1)->toolchanges_depth() + m_perimeter_width;
//debugging:
/* float oldx = writer.x();
float oldy = writer.y();
writer.travel(xr,sparse_beginning_y);
writer.extrude(xr+5,writer.y());
writer.travel(oldx,oldy);*/
float sum_of_depths = 0.f;
for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
if (tch.old_tool == m_current_tool) {
@ -941,13 +944,6 @@ void WipeTower::toolchange_Unload(
float ramming_end_y = sum_of_depths;
ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
// debugging:
/*float oldx = writer.x();
float oldy = writer.y();
writer.travel(xl,ramming_end_y);
writer.extrude(xl-15,writer.y());
writer.travel(oldx,oldy);*/
if ( (m_current_shape == SHAPE_REVERSED && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width ) ||
(m_current_shape == SHAPE_NORMAL && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width ) )
{
@ -998,12 +994,6 @@ void WipeTower::toolchange_Unload(
.retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
.retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
.retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
/*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
.load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
.load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
.resume_preview();
}
// Wipe tower should only change temperature with single extruder MM. Otherwise, all temperatures should
@ -1096,11 +1086,6 @@ void WipeTower::toolchange_Load(
writer.append("; CP TOOLCHANGE LOAD\n")
.suppress_preview()
/*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
.load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
.load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
.load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
@ -1170,11 +1155,14 @@ void WipeTower::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
}
// this is neither priming nor not the last toolchange on this layer - we are going back to the model - wipe the nozzle
// this is neither priming nor not the last toolchange on this layer - we are
// going back to the model - wipe the nozzle.
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
m_left_to_right = !m_left_to_right;
writer.travel(writer.x(), writer.y() - dy)
.travel(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
writer.add_wipe_point(writer.x(), writer.y())
.add_wipe_point(writer.x(), writer.y() - dy)
.add_wipe_point(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy);
}
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
@ -1238,7 +1226,8 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y());
}
writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
writer.add_wipe_point(writer.x(), writer.y())
.add_wipe_point(box.rd.x()-m_perimeter_width/2.f,writer.y());
}
else { // Extrude a sparse infill to support the material to be printed above.
const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
@ -1257,10 +1246,13 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
writer.travel(x,writer.y());
writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y());
}
writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
writer.add_wipe_point(Vec2f(writer.x(), writer.y()))
.add_wipe_point(Vec2f(left, writer.y()));
}
else {
writer.add_wipe_point(Vec2f(writer.x(), writer.y()))
.add_wipe_point(Vec2f(right, writer.y()));
}
else
writer.travel(right,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
}
writer.append("; CP EMPTY GRID END\n"
";------------------\n\n\n\n\n\n\n");
@ -1285,6 +1277,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
result.extrusions = writer.extrusions();
result.start_pos = writer.start_pos_rotated();
result.end_pos = writer.pos_rotated();
result.wipe_path = writer.wipe_path();
return result;
}
@ -1356,7 +1349,7 @@ void WipeTower::save_on_last_wipe()
continue;
for (const auto &toolchange : m_layer_info->tool_changes)
tool_change(toolchange.new_tool, false);
tool_change(toolchange.new_tool);
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
@ -1418,7 +1411,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
for (const auto &toolchange : layer.tool_changes)
layer_result.emplace_back(tool_change(toolchange.new_tool, false));
layer_result.emplace_back(tool_change(toolchange.new_tool));
if (! layer_finished()) {
auto finish_layer_toolchange = finish_layer();
@ -1432,6 +1425,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
last_toolchange.gcode += finish_layer_toolchange.gcode;
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
last_toolchange.end_pos = finish_layer_toolchange.end_pos;
last_toolchange.wipe_path = finish_layer_toolchange.wipe_path;
}
else
layer_result.emplace_back(std::move(finish_layer_toolchange));
@ -1467,4 +1461,4 @@ void WipeTower::make_wipe_tower_square()
lay.extra_spacing = lay.depth / lay.toolchanges_depth();
}
}; // namespace Slic3r
} // namespace Slic3r

View file

@ -57,6 +57,13 @@ public:
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
bool priming;
// Pass a polyline so that normal G-code generator can do a wipe for us.
// The wipe cannot be done by the wipe tower because it has to pass back
// a loaded extruder, so it would have to either do a wipe with no retraction
// (leading to https://github.com/prusa3d/PrusaSlicer/issues/2834) or do
// an extra retraction-unretraction pair.
std::vector<Vec2f> wipe_path;
// Initial tool
int initial_tool;
@ -154,7 +161,7 @@ public:
// Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first.
ToolChangeResult tool_change(size_t new_tool, bool last_in_layer);
ToolChangeResult tool_change(size_t new_tool);
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.

View file

@ -153,7 +153,7 @@ GCodeSender::set_baud_rate(unsigned int baud_rate)
if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0)
printf("Failed to set baud rate: %s\n", strerror(errno));
#else
//throw invalid_argument ("OS does not currently support custom bauds");
//throw Slic3r::InvalidArgument("OS does not currently support custom bauds");
#endif
}
}

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "GCodeTimeEstimator.hpp"
#include "Utils.hpp"
#include <boost/bind.hpp>
@ -254,13 +255,13 @@ namespace Slic3r {
{
boost::nowide::ifstream in(filename);
if (!in.good())
throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
std::string path_tmp = filename + ".postprocess";
FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb");
if (out == nullptr)
throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
std::string normal_time_mask = "M73 P%s R%s\n";
std::string silent_time_mask = "M73 Q%s S%s\n";
@ -278,7 +279,7 @@ namespace Slic3r {
in.close();
fclose(out);
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
}
export_line.clear();
};
@ -326,7 +327,7 @@ namespace Slic3r {
if (!in.good())
{
fclose(out);
throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
}
// check tags
@ -383,7 +384,7 @@ namespace Slic3r {
in.close();
if (rename_file(path_tmp, filename))
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
"Is " + path_tmp + " locked?" + '\n');
return true;

View file

@ -1,4 +1,5 @@
#include "libslic3r.h"
#include "Exception.hpp"
#include "Geometry.hpp"
#include "ClipperUtils.hpp"
#include "ExPolygon.hpp"
@ -471,7 +472,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo
size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1)));
if (num_parts > cellw * cellh)
throw std::invalid_argument("%zu parts won't fit in your print area!\n", num_parts);
throw Slic3r::InvalidArgument("%zu parts won't fit in your print area!\n", num_parts);
// Get a bounding box of cellw x cellh cells, centered at the center of the bed.
Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);

View file

@ -281,7 +281,7 @@ bool directions_parallel(double angle1, double angle2, double max_diff = 0);
template<class T> bool contains(const std::vector<T> &vector, const Point &point);
template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
double rad2deg_dir(double angle);
template<typename T> T deg2rad(T angle) { return T(PI) * angle / T(180.0); }
template<typename T> constexpr T deg2rad(const T angle) { return T(PI) * angle / T(180.0); }
template<typename T> T angle_to_0_2PI(T angle)
{
static const T TWO_PI = T(2) * T(PI);

View file

@ -13,7 +13,7 @@ class Layer;
class PrintRegion;
class PrintObject;
namespace FillAdaptive_Internal {
namespace FillAdaptive {
struct Octree;
};
@ -139,7 +139,7 @@ public:
}
void make_perimeters();
void make_fills() { this->make_fills(nullptr, nullptr); };
void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, FillAdaptive_Internal::Octree* support_fill_octree);
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree);
void make_ironing();
void export_region_slices_to_svg(const char *path) const;

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "MeshBoolean.hpp"
#include "libslic3r/TriangleMesh.hpp"
#undef PI
@ -136,7 +137,7 @@ template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &o
if(CGAL::is_closed(out))
CGALProc::orient_to_bound_a_volume(out);
else
std::runtime_error("Mesh not watertight");
throw Slic3r::RuntimeError("Mesh not watertight");
}
inline Vec3d to_vec3d(const _EpicMesh::Point &v)
@ -222,7 +223,7 @@ template<class Op> void _cgal_do(Op &&op, CGALMesh &A, CGALMesh &B)
}
if (! success)
throw std::runtime_error("CGAL mesh boolean operation failed.");
throw Slic3r::RuntimeError("CGAL mesh boolean operation failed.");
}
void minus(CGALMesh &A, CGALMesh &B) { _cgal_do(_cgal_diff, A, B); }

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "Model.hpp"
#include "ModelArrange.hpp"
#include "Geometry.hpp"
@ -116,13 +117,13 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
else if (boost::algorithm::iends_with(input_file, ".prusa"))
result = load_prus(input_file.c_str(), &model);
else
throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
if (! result)
throw std::runtime_error("Loading of a model file failed.");
throw Slic3r::RuntimeError("Loading of a model file failed.");
if (model.objects.empty())
throw std::runtime_error("The supplied file couldn't be read because it's empty");
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
for (ModelObject *o : model.objects)
o->input_file = input_file;
@ -146,13 +147,13 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
result = load_amf(input_file.c_str(), config, &model, check_version);
else
throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension.");
throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension.");
if (!result)
throw std::runtime_error("Loading of a model file failed.");
throw Slic3r::RuntimeError("Loading of a model file failed.");
if (model.objects.empty())
throw std::runtime_error("The supplied file couldn't be read because it's empty");
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
for (ModelObject *o : model.objects)
{
@ -776,6 +777,38 @@ TriangleMesh ModelObject::raw_mesh() const
return mesh;
}
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D plater
// and to display the object statistics at ModelObject::print_info().
indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
{
size_t num_vertices = 0;
size_t num_faces = 0;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
num_vertices += v->mesh().its.vertices.size();
num_faces += v->mesh().its.indices.size();
}
indexed_triangle_set out;
out.vertices.reserve(num_vertices);
out.indices.reserve(num_faces);
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
size_t i = out.vertices.size();
size_t j = out.indices.size();
append(out.vertices, v->mesh().its.vertices);
append(out.indices, v->mesh().its.indices);
auto m = v->get_matrix();
for (; i < out.vertices.size(); ++ i)
out.vertices[i] = (m * out.vertices[i].cast<double>()).cast<float>().eval();
if (v->is_left_handed()) {
for (; j < out.indices.size(); ++ j)
std::swap(out.indices[j][0], out.indices[j][1]);
}
}
return out;
}
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh ModelObject::full_raw_mesh() const
{
@ -817,7 +850,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
m_raw_bounding_box_valid = true;
m_raw_bounding_box.reset();
if (this->instances.empty())
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
throw Slic3r::InvalidArgument("Can't call raw_bounding_box() with no instances");
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
for (const ModelVolume *v : this->volumes)

View file

@ -244,6 +244,8 @@ public:
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
// Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D plater.
TriangleMesh raw_mesh() const;
// The same as above, but producing a lightweight indexed_triangle_set.
indexed_triangle_set raw_indexed_triangle_set() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh full_raw_mesh() const;
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.

View file

@ -20,7 +20,7 @@ using VirtualBedFn = std::function<void(arrangement::ArrangePolygon&)>;
[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&)
{
throw std::runtime_error("Objects could not fit on the bed");
throw Slic3r::RuntimeError("Objects could not fit on the bed");
}
ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances);

View file

@ -64,11 +64,6 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh,
float interiorBandWidth,
int flags)
{
// openvdb::initialize();
// return openvdb::tools::meshToVolume<openvdb::FloatGrid>(
// TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth,
// interiorBandWidth, flags);
openvdb::initialize();
TriangleMeshPtrs meshparts = mesh.split();
@ -83,15 +78,23 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh,
openvdb::FloatGrid::Ptr grid;
for (TriangleMesh *m : meshparts) {
auto gridptr = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
auto subgrid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
TriangleMeshDataAdapter{*m}, tr, exteriorBandWidth,
interiorBandWidth, flags);
if (grid && gridptr) openvdb::tools::csgUnion(*grid, *gridptr);
else if (gridptr) grid = std::move(gridptr);
if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid);
else if (subgrid) grid = std::move(subgrid);
}
grid = openvdb::tools::levelSetRebuild(*grid, 0., exteriorBandWidth, interiorBandWidth);
if (grid) {
grid = openvdb::tools::levelSetRebuild(*grid, 0., exteriorBandWidth,
interiorBandWidth);
} else if(meshparts.empty()) {
// Splitting failed, fall back to hollow the original mesh
grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth,
interiorBandWidth, flags);
}
return grid;
}

View file

@ -1,4 +1,5 @@
#include "PlaceholderParser.hpp"
#include "Exception.hpp"
#include "Flow.hpp"
#include <cstring>
#include <ctime>
@ -1303,7 +1304,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
if (!context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n';
throw std::runtime_error(context.error_message);
throw Slic3r::RuntimeError(context.error_message);
}
return output;
}
@ -1319,7 +1320,7 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu
}
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
// Throws std::runtime_error on syntax or runtime error.
// Throws Slic3r::RuntimeError on syntax or runtime error.
bool PlaceholderParser::evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override)
{
client::MyContext context;

View file

@ -40,11 +40,11 @@ public:
const DynamicConfig* external_config() const { return m_external_config; }
// Fill in the template using a macro processing language.
// Throws std::runtime_error on syntax or runtime error.
// Throws Slic3r::RuntimeError on syntax or runtime error.
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const;
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
// Throws std::runtime_error on syntax or runtime error.
// Throws Slic3r::RuntimeError on syntax or runtime error.
static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr);
// Update timestamp, year, month, day, hour, minute, second variables at the provided config.

View file

@ -44,16 +44,6 @@ Pointf3s transform(const Pointf3s& points, const Transform3d& t)
return ret_points;
}
void Point::rotate(double angle)
{
double cur_x = (double)(*this)(0);
double cur_y = (double)(*this)(1);
double s = ::sin(angle);
double c = ::cos(angle);
(*this)(0) = (coord_t)round(c * cur_x - s * cur_y);
(*this)(1) = (coord_t)round(c * cur_y + s * cur_x);
}
void Point::rotate(double angle, const Point &center)
{
double cur_x = (double)(*this)(0);

View file

@ -105,6 +105,7 @@ public:
template<typename OtherDerived>
Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
static Point new_scale(const Vec2d &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
// This method allows you to assign Eigen expressions to MyVectorType
template<typename OtherDerived>
@ -121,7 +122,14 @@ public:
Point& operator*=(const double &rhs) { (*this)(0) = coord_t((*this)(0) * rhs); (*this)(1) = coord_t((*this)(1) * rhs); return *this; }
Point operator*(const double &rhs) { return Point((*this)(0) * rhs, (*this)(1) * rhs); }
void rotate(double angle);
void rotate(double angle) { this->rotate(std::cos(angle), std::sin(angle)); }
void rotate(double cos_a, double sin_a) {
double cur_x = (double)(*this)(0);
double cur_y = (double)(*this)(1);
(*this)(0) = (coord_t)round(cos_a * cur_x - sin_a * cur_y);
(*this)(1) = (coord_t)round(cos_a * cur_y + sin_a * cur_x);
}
void rotate(double angle, const Point &center);
Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; }
Point rotated(double angle, const Point &center) const { Point res(*this); res.rotate(angle, center); return res; }

View file

@ -1,5 +1,6 @@
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Exception.hpp"
#include "Polygon.hpp"
#include "Polyline.hpp"
@ -16,7 +17,7 @@ Polyline Polygon::split_at_vertex(const Point &point) const
for (const Point &pt : this->points)
if (pt == point)
return this->split_at_index(int(&pt - &this->points.front()));
throw std::invalid_argument("Point not found");
throw Slic3r::InvalidArgument("Point not found");
return Polyline();
}
@ -259,6 +260,44 @@ Point Polygon::point_projection(const Point &point) const
return proj;
}
std::vector<float> Polygon::parameter_by_length() const
{
// Parametrize the polygon by its length.
std::vector<float> lengths(points.size()+1, 0.);
for (size_t i = 1; i < points.size(); ++ i)
lengths[i] = lengths[i-1] + (points[i] - points[i-1]).cast<float>().norm();
lengths.back() = lengths[lengths.size()-2] + (points.front() - points.back()).cast<float>().norm();
return lengths;
}
void Polygon::densify(float min_length, std::vector<float>* lengths_ptr)
{
std::vector<float> lengths_local;
std::vector<float>& lengths = lengths_ptr ? *lengths_ptr : lengths_local;
if (! lengths_ptr) {
// Length parametrization has not been provided. Calculate our own.
lengths = this->parameter_by_length();
}
assert(points.size() == lengths.size() - 1);
for (size_t j=1; j<=points.size(); ++j) {
bool last = j == points.size();
int i = last ? 0 : j;
if (lengths[j] - lengths[j-1] > min_length) {
Point diff = points[i] - points[j-1];
float diff_len = lengths[j] - lengths[j-1];
float r = (min_length/diff_len);
Point new_pt = points[j-1] + Point(r*diff[0], r*diff[1]);
points.insert(points.begin() + j, new_pt);
lengths.insert(lengths.begin() + j, lengths[j-1] + min_length);
}
}
assert(points.size() == lengths.size() - 1);
}
BoundingBox get_extents(const Points &points)
{
return BoundingBox(points);

View file

@ -61,12 +61,14 @@ public:
bool contains(const Point &point) const;
Polygons simplify(double tolerance) const;
void simplify(double tolerance, Polygons &polygons) const;
void densify(float min_length, std::vector<float>* lengths = nullptr);
void triangulate_convex(Polygons* polygons) const;
Point centroid() const;
Points concave_points(double angle = PI) const;
Points convex_points(double angle = PI) const;
// Projection of a point onto the polygon.
Point point_projection(const Point &point) const;
std::vector<float> parameter_by_length() const;
};
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }

View file

@ -1,5 +1,6 @@
#include "BoundingBox.hpp"
#include "Polyline.hpp"
#include "Exception.hpp"
#include "ExPolygon.hpp"
#include "ExPolygonCollection.hpp"
#include "Line.hpp"
@ -19,7 +20,7 @@ Polyline::operator Polylines() const
Polyline::operator Line() const
{
if (this->points.size() > 2)
throw std::invalid_argument("Can't convert polyline with more than two points to a line");
throw Slic3r::InvalidArgument("Can't convert polyline with more than two points to a line");
return Line(this->points.front(), this->points.back());
}
@ -207,7 +208,7 @@ BoundingBox get_extents(const Polylines &polylines)
const Point& leftmost_point(const Polylines &polylines)
{
if (polylines.empty())
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
throw Slic3r::InvalidArgument("leftmost_point() called on empty PolylineCollection");
Polylines::const_iterator it = polylines.begin();
const Point *p = &it->leftmost_point();
for (++ it; it != polylines.end(); ++it) {

View file

@ -1,5 +1,6 @@
#include <cassert>
#include "Exception.hpp"
#include "Preset.hpp"
#include "AppConfig.hpp"
@ -107,7 +108,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
const std::string id = path.stem().string();
if (! boost::filesystem::exists(path)) {
throw std::runtime_error((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
throw Slic3r::RuntimeError((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
}
VendorProfile res(id);
@ -117,7 +118,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
{
auto res = tree.find(key);
if (res == tree.not_found()) {
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
}
return res;
};
@ -129,7 +130,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data();
auto config_version = Semver::parse(config_version_str);
if (! config_version) {
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
} else {
res.config_version = std::move(*config_version);
}
@ -672,9 +673,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
preset.loaded = true;
} catch (const std::ifstream::failure &err) {
throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
} catch (const std::runtime_error &err) {
throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
}
presets_loaded.emplace_back(preset);
} catch (const std::runtime_error &err) {
@ -686,7 +687,7 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(first_visible_idx());
if (! errors_cummulative.empty())
throw std::runtime_error(errors_cummulative);
throw Slic3r::RuntimeError(errors_cummulative);
}
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
@ -1557,10 +1558,10 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const
printer.loaded = true;
}
catch (const std::ifstream::failure& err) {
throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what());
throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what());
}
catch (const std::runtime_error& err) {
throw std::runtime_error(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what());
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what());
}
printers_loaded.emplace_back(printer);
}
@ -1572,7 +1573,7 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const
m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end()));
std::sort(m_printers.begin(), m_printers.end());
if (!errors_cummulative.empty())
throw std::runtime_error(errors_cummulative);
throw Slic3r::RuntimeError(errors_cummulative);
}
// if there is saved user presets, contains information about "Print Host upload",

View file

@ -157,7 +157,7 @@ void PresetBundle::setup_directories()
subdir.make_preferred();
if (! boost::filesystem::is_directory(subdir) &&
! boost::filesystem::create_directory(subdir))
throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string());
throw Slic3r::RuntimeError(std::string("Slic3r was unable to create its data directory at ") + subdir.string());
}
}
@ -207,7 +207,7 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_
this->update_multi_material_filament_presets();
this->update_compatible(PresetSelectCompatibleType::Never);
if (! errors_cummulative.empty())
throw std::runtime_error(errors_cummulative);
throw Slic3r::RuntimeError(errors_cummulative);
this->load_selections(config, preferred_model_id);
}
@ -679,21 +679,21 @@ void PresetBundle::load_config_file(const std::string &path)
boost::nowide::ifstream ifs(path);
boost::property_tree::read_ini(ifs, tree);
} catch (const std::ifstream::failure &err) {
throw std::runtime_error(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
} catch (const boost::property_tree::file_parser_error &err) {
throw std::runtime_error((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%")
throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%")
% err.filename() % err.message() % err.line()).str());
} catch (const std::runtime_error &err) {
throw std::runtime_error(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
}
// 2) Continue based on the type of the configuration file.
ConfigFileType config_file_type = guess_config_file_type(tree);
switch (config_file_type) {
case CONFIG_FILE_TYPE_UNKNOWN:
throw std::runtime_error(std::string("Unknown configuration file type: ") + path);
throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path);
case CONFIG_FILE_TYPE_APP_CONFIG:
throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file.");
throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file.");
case CONFIG_FILE_TYPE_CONFIG:
{
// Initialize a config from full defaults.

View file

@ -1,5 +1,6 @@
#include "clipper/clipper_z.hpp"
#include "Exception.hpp"
#include "Print.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
@ -1507,7 +1508,7 @@ BoundingBox Print::total_bounding_box() const
double Print::skirt_first_layer_height() const
{
if (m_objects.empty())
throw std::invalid_argument("skirt_first_layer_height() can't be called without PrintObjects");
throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects");
return m_objects.front()->config().get_abs_value("first_layer_height");
}
@ -1583,7 +1584,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
// Slicing process, running at a background thread.
void Print::process()
{
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info();
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
obj->make_perimeters();
this->set_status(70, L("Infilling layers"));
@ -1603,7 +1604,7 @@ void Print::process()
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
m_tool_ordering = ToolOrdering(*this, -1, false);
if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
throw std::runtime_error("The print is empty. The model is not printable with current print settings.");
throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings.");
}
this->set_done(psWipeTower);
}
@ -2173,7 +2174,7 @@ void Print::_make_wipe_tower()
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
}
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
wipe_tower.tool_change((unsigned int)-1, false));
wipe_tower.tool_change((unsigned int)(-1)));
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();

View file

@ -30,8 +30,10 @@ enum class SlicingMode : uint32_t;
class Layer;
class SupportLayer;
namespace FillAdaptive_Internal {
namespace FillAdaptive {
struct Octree;
struct OctreeDeleter;
using OctreePtr = std::unique_ptr<Octree, OctreeDeleter>;
};
// Print step IDs for keeping track of the print state.
@ -239,7 +241,7 @@ private:
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
std::pair<std::unique_ptr<FillAdaptive_Internal::Octree>, std::unique_ptr<FillAdaptive_Internal::Octree>> prepare_adaptive_infill_data();
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data();
// XYZ in scaled coordinates
Vec3crd m_size;

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "PrintBase.hpp"
#include <boost/filesystem.hpp>
@ -68,7 +69,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
filename = boost::filesystem::change_extension(filename, default_ext);
return filename.string();
} catch (std::runtime_error &err) {
throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
throw Slic3r::RuntimeError(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
}
}

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "Print.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
@ -8,6 +9,7 @@
#include "SupportMaterial.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
#include "Tesselate.hpp"
#include "Utils.hpp"
#include "AABBTreeIndirect.hpp"
#include "Fill/FillAdaptive.hpp"
@ -138,7 +140,7 @@ void PrintObject::slice()
}
});
if (m_layers.empty())
throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
this->set_done(posSlice);
}
@ -426,81 +428,52 @@ void PrintObject::generate_support_material()
// therefore they cannot be printed without supports.
for (const Layer *layer : m_layers)
if (layer->empty())
throw std::runtime_error("Levitating objects cannot be printed without supports.");
throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
#endif
}
this->set_done(posSupportMaterial);
}
}
//#define ADAPTIVE_SUPPORT_SIMPLE
std::pair<std::unique_ptr<FillAdaptive_Internal::Octree>, std::unique_ptr<FillAdaptive_Internal::Octree>> PrintObject::prepare_adaptive_infill_data()
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare_adaptive_infill_data()
{
using namespace FillAdaptive_Internal;
using namespace FillAdaptive;
auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this);
if (adaptive_line_spacing == 0. && support_line_spacing == 0. || this->layers().empty())
return std::make_pair(OctreePtr(), OctreePtr());
std::unique_ptr<Octree> adaptive_fill_octree = {}, support_fill_octree = {};
indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set();
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes.
Transform3d m = m_trafo;
m.pretranslate(Vec3d(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0));
auto to_octree = transform_to_octree().toRotationMatrix();
its_transform(mesh, to_octree * m, true);
if (adaptive_line_spacing == 0. && support_line_spacing == 0.)
return std::make_pair(std::move(adaptive_fill_octree), std::move(support_fill_octree));
// Triangulate internal bridging surfaces.
std::vector<std::vector<Vec3d>> overhangs(this->layers().size());
tbb::parallel_for(
tbb::blocked_range<int>(0, int(m_layers.size()) - 1),
[this, &to_octree, &overhangs](const tbb::blocked_range<int> &range) {
std::vector<Vec3d> &out = overhangs[range.begin()];
for (int idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
const Layer *layer = this->layers()[idx_layer];
for (const LayerRegion *layerm : layer->regions())
for (const Surface &surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternalBridge)
append(out, triangulate_expolygon_3d(surface.expolygon, layer->bottom_z()));
}
for (Vec3d &p : out)
p = (to_octree * p).eval();
});
// and gather them.
for (size_t i = 1; i < overhangs.size(); ++ i)
append(overhangs.front(), std::move(overhangs[i]));
TriangleMesh mesh = this->model_object()->raw_mesh();
mesh.transform(m_trafo, true);
// Apply XY shift
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// Center of the first cube in octree
Vec3d mesh_origin = mesh.bounding_box().center();
#ifdef ADAPTIVE_SUPPORT_SIMPLE
if (mesh.its.vertices.empty())
{
mesh.require_shared_vertices();
}
Vec3f vertical(0, 0, 1);
indexed_triangle_set its_set;
its_set.vertices = mesh.its.vertices;
// Filter out non overhanging faces
for (size_t i = 0; i < mesh.its.indices.size(); ++i) {
stl_triangle_vertex_indices vertex_idx = mesh.its.indices[i];
auto its_calculate_normal = [](const stl_triangle_vertex_indices &index, const std::vector<stl_vertex> &vertices) {
stl_normal normal = (vertices[index.y()] - vertices[index.x()]).cross(vertices[index.z()] - vertices[index.x()]);
return normal;
};
stl_normal normal = its_calculate_normal(vertex_idx, mesh.its.vertices);
stl_normalize_vector(normal);
if(normal.dot(vertical) >= 0.707) {
its_set.indices.push_back(vertex_idx);
}
}
mesh = TriangleMesh(its_set);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
Slic3r::store_stl(debug_out_path("overhangs.stl").c_str(), &mesh, false);
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
#endif /* ADAPTIVE_SUPPORT_SIMPLE */
Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0);
Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()).inverse();
if (adaptive_line_spacing != 0.) {
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes
mesh.transform(rotation_matrix);
adaptive_fill_octree = FillAdaptive::build_octree(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin);
}
if (support_line_spacing != 0.)
support_fill_octree = FillSupportCubic::build_octree(mesh, support_line_spacing, rotation_matrix * mesh_origin, rotation_matrix);
return std::make_pair(std::move(adaptive_fill_octree), std::move(support_fill_octree));
return std::make_pair(
adaptive_line_spacing ? build_octree(mesh, overhangs.front(), adaptive_line_spacing, false) : OctreePtr(),
support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr());
}
void PrintObject::clear_layers()
@ -2836,8 +2809,8 @@ void PrintObject::project_and_append_custom_facets(
// Calculate how to move points on triangle sides per unit z increment.
Vec2f ta(trianglef[1] - trianglef[0]);
Vec2f tb(trianglef[2] - trianglef[0]);
ta *= 1./(facet[1].z() - facet[0].z());
tb *= 1./(facet[2].z() - facet[0].z());
ta *= 1.f/(facet[1].z() - facet[0].z());
tb *= 1.f/(facet[2].z() - facet[0].z());
// Projection on current slice will be build directly in place.
LightPolygon* proj = &projections_of_triangles[idx].polygons[0];
@ -2848,7 +2821,7 @@ void PrintObject::project_and_append_custom_facets(
// Project a sub-polygon on all slices intersecting the triangle.
while (it != layers().end()) {
const float z = (*it)->slice_z;
const float z = float((*it)->slice_z);
// Projections of triangle sides intersections with slices.
// a moves along one side, b tracks the other.
@ -2860,7 +2833,7 @@ void PrintObject::project_and_append_custom_facets(
if (z > facet[1].z() && ! passed_first) {
proj->add(trianglef[1]);
ta = trianglef[2]-trianglef[1];
ta *= 1./(facet[2].z() - facet[1].z());
ta *= 1.f/(facet[2].z() - facet[1].z());
passed_first = true;
}

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "Print.hpp"
namespace Slic3r {
@ -13,7 +14,7 @@ unsigned int PrintRegion::extruder(FlowRole role) const
else if (role == frSolidInfill || role == frTopSolidInfill)
extruder = m_config.solid_infill_extruder;
else
throw std::invalid_argument("Unknown role");
throw Slic3r::InvalidArgument("Unknown role");
return extruder;
}
@ -40,7 +41,7 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
} else if (role == frTopSolidInfill) {
config_width = m_config.top_infill_extrusion_width;
} else {
throw std::invalid_argument("Unknown role");
throw Slic3r::InvalidArgument("Unknown role");
}
}

View file

@ -1,3 +1,4 @@
#include <libslic3r/Exception.hpp>
#include <libslic3r/SLAPrintSteps.hpp>
#include <libslic3r/MeshBoolean.hpp>
@ -187,7 +188,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
}
if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal))
throw std::runtime_error(L("Too much overlapping holes."));
throw Slic3r::SlicingError(L("Too many overlapping holes."));
auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh);
@ -195,7 +196,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal);
hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
} catch (const std::runtime_error &) {
throw std::runtime_error(L(
throw Slic3r::SlicingError(L(
"Drilling holes into the mesh failed. "
"This is usually caused by broken model. Try to fix it first."));
}
@ -241,7 +242,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
if(slindex_it == po.m_slice_index.end())
//TRN To be shown at the status bar on SLA slicing error.
throw std::runtime_error(
throw Slic3r::RuntimeError(
L("Slicing had to be stopped due to an internal error: "
"Inconsistent slice index."));
@ -445,7 +446,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad);
if (!validate_pad(pad_mesh, pcfg))
throw std::runtime_error(
throw Slic3r::SlicingError(
L("No pad can be generated for this model with the "
"current configuration"));
@ -613,7 +614,7 @@ void SLAPrint::Steps::initialize_printer_input()
for(const SliceRecord& slicerecord : o->get_slice_index()) {
if (!slicerecord.is_valid())
throw std::runtime_error(
throw Slic3r::SlicingError(
L("There are unprintable objects. Try to "
"adjust support settings to make the "
"objects printable."));

View file

@ -10,6 +10,8 @@
#include "semver/semver.h"
#include "Exception.hpp"
namespace Slic3r {
@ -38,7 +40,7 @@ public:
{
auto parsed = parse(str);
if (! parsed) {
throw std::runtime_error(std::string("Could not parse version string: ") + str);
throw Slic3r::RuntimeError(std::string("Could not parse version string: ") + str);
}
ver = parsed->ver;
parsed->ver = semver_zero();

View file

@ -1973,4 +1973,59 @@ std::vector<const PrintInstance*> chain_print_object_instances(const Print &prin
return out;
}
Polylines chain_lines(const std::vector<Line> &lines, const double point_distance_epsilon)
{
// Create line end point lookup.
struct LineEnd {
LineEnd(const Line *line, bool start) : line(line), start(start) {}
const Line *line;
// Is it the start or end point?
bool start;
const Point& point() const { return start ? line->a : line->b; }
const Point& other_point() const { return start ? line->b : line->a; }
LineEnd other_end() const { return LineEnd(line, ! start); }
bool operator==(const LineEnd &rhs) const { return this->line == rhs.line && this->start == rhs.start; }
};
struct LineEndAccessor {
const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
};
typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
ClosestPointLookupType closest_end_point_lookup(point_distance_epsilon);
for (const Line &line : lines) {
closest_end_point_lookup.insert(LineEnd(&line, true));
closest_end_point_lookup.insert(LineEnd(&line, false));
}
// Chain the lines.
std::vector<char> line_consumed(lines.size(), false);
static const double point_distance_epsilon2 = point_distance_epsilon * point_distance_epsilon;
Polylines out;
for (const Line &seed : lines)
if (! line_consumed[&seed - lines.data()]) {
line_consumed[&seed - lines.data()] = true;
closest_end_point_lookup.erase(LineEnd(&seed, false));
closest_end_point_lookup.erase(LineEnd(&seed, true));
Polyline pl { seed.a, seed.b };
for (size_t round = 0; round < 2; ++ round) {
for (;;) {
auto [line_end, dist2] = closest_end_point_lookup.find(pl.last_point());
if (line_end == nullptr || dist2 >= point_distance_epsilon2)
// Cannot extent in this direction.
break;
// Average the last point.
pl.points.back() = (0.5 * (pl.points.back().cast<double>() + line_end->point().cast<double>())).cast<coord_t>();
// and extend with the new line segment.
pl.points.emplace_back(line_end->other_point());
closest_end_point_lookup.erase(*line_end);
closest_end_point_lookup.erase(line_end->other_end());
line_consumed[line_end->line - lines.data()] = true;
}
// reverse and try the oter direction.
pl.reverse();
}
out.emplace_back(std::move(pl));
}
return out;
}
} // namespace Slic3r

View file

@ -33,6 +33,8 @@ class Print;
struct PrintInstance;
std::vector<const PrintInstance*> chain_print_object_instances(const Print &print);
// Chain lines into polylines.
Polylines chain_lines(const std::vector<Line> &lines, const double point_distance_epsilon);
} // namespace Slic3r

View file

@ -1,3 +1,4 @@
#include "Exception.hpp"
#include "TriangleMesh.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
@ -420,7 +421,7 @@ std::deque<uint32_t> TriangleMesh::find_unvisited_neighbors(std::vector<unsigned
{
// Make sure we're not operating on a broken mesh.
if (!this->repaired)
throw std::runtime_error("find_unvisited_neighbors() requires repair()");
throw Slic3r::RuntimeError("find_unvisited_neighbors() requires repair()");
// If the visited list is empty, populate it with false for every facet.
if (facet_visited.empty())
@ -683,7 +684,7 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac
{
mesh = _mesh;
if (! mesh->has_shared_vertices())
throw std::invalid_argument("TriangleMeshSlicer was passed a mesh without shared vertices.");
throw Slic3r::InvalidArgument("TriangleMeshSlicer was passed a mesh without shared vertices.");
throw_on_cancel();
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);

View file

@ -348,11 +348,11 @@ inline std::string get_time_dhm(float time_in_secs)
char buffer[64];
if (days > 0)
::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
::sprintf(buffer, "%dd %dh %dm", days, hours, minutes);
else if (hours > 0)
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
::sprintf(buffer, "%dh %dm", hours, minutes);
else if (minutes > 0)
::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
::sprintf(buffer, "%dm", minutes);
return buffer;
}

View file

@ -1,5 +1,6 @@
#include <exception>
#include "Exception.hpp"
#include "Zipper.hpp"
#include "miniz_extension.hpp"
#include <boost/log/trivial.hpp>
@ -29,7 +30,7 @@ public:
SLIC3R_NORETURN void blow_up() const
{
throw std::runtime_error(formatted_errorstr());
throw Slic3r::RuntimeError(formatted_errorstr());
}
bool is_alive()

View file

@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "@SLIC3R_APP_NAME@ G-code Viewer"
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranelucci"
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
VALUE "OriginalFilename", "prusa-gcodeviewer.exe"
}
}

View file

@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
VALUE "ProductName", "@SLIC3R_APP_NAME@"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "@SLIC3R_APP_NAME@"
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranelucci"
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
VALUE "OriginalFilename", "prusa-slicer.exe"
}
}

View file

@ -315,7 +315,7 @@ size_t SnapshotDB::load_db()
// Sort the snapshots by their date/time.
std::sort(m_snapshots.begin(), m_snapshots.end(), [](const Snapshot &s1, const Snapshot &s2) { return s1.time_captured < s2.time_captured; });
if (! errors_cummulative.empty())
throw std::runtime_error(errors_cummulative);
throw Slic3r::RuntimeError(errors_cummulative);
return m_snapshots.size();
}
@ -339,7 +339,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src
{
if (! boost::filesystem::is_directory(path_dst) &&
! boost::filesystem::create_directory(path_dst))
throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string());
throw Slic3r::RuntimeError(std::string("Slic3r was unable to create a directory at ") + path_dst.string());
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
if (Slic3r::is_ini_file(dir_entry))
@ -429,7 +429,7 @@ const Snapshot& SnapshotDB::restore_snapshot(const std::string &id, AppConfig &a
this->restore_snapshot(snapshot, app_config);
return snapshot;
}
throw std::runtime_error(std::string("Snapshot with id " + id + " was not found."));
throw Slic3r::RuntimeError(std::string("Snapshot with id " + id + " was not found."));
}
void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_config)
@ -501,7 +501,7 @@ boost::filesystem::path SnapshotDB::create_db_dir()
subdir.make_preferred();
if (! boost::filesystem::is_directory(subdir) &&
! boost::filesystem::create_directory(subdir))
throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + subdir.string());
throw Slic3r::RuntimeError(std::string("Slic3r was unable to create a directory at ") + subdir.string());
}
return snapshots_dir;
}

View file

@ -324,7 +324,7 @@ std::vector<Index> Index::load_db()
}
if (! errors_cummulative.empty())
throw std::runtime_error(errors_cummulative);
throw Slic3r::RuntimeError(errors_cummulative);
return index_db;
}

View file

@ -1926,7 +1926,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity,
if (extrusion_entity_collection != nullptr)
extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume);
else {
throw std::runtime_error("Unexpected extrusion_entity type in to_verts()");
throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()");
}
}
}

View file

@ -41,6 +41,36 @@
namespace Slic3r {
bool SlicingProcessCompletedEvent::critical_error() const
{
try {
this->rethrow_exception();
} catch (const Slic3r::SlicingError &ex) {
// Exception derived from SlicingError is non-critical.
return false;
} catch (...) {
}
return true;
}
std::string SlicingProcessCompletedEvent::format_error_message() const
{
std::string error;
try {
this->rethrow_exception();
} catch (const std::bad_alloc& ex) {
wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it."))) % SLIC3R_APP_NAME).str());
error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what());
} catch (std::exception &ex) {
error = ex.what();
} catch (...) {
error = "Unknown C++ exception.";
}
return error;
}
BackgroundSlicingProcess::BackgroundSlicingProcess()
{
boost::filesystem::path temp_path(wxStandardPaths::Get().GetTempDir().utf8_str().data());
@ -109,19 +139,19 @@ void BackgroundSlicingProcess::process_fff()
switch (copy_ret_val) {
case SUCCESS: break; // no error
case FAIL_COPY_FILE:
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
break;
case FAIL_FILES_DIFFERENT:
throw std::runtime_error((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
break;
case FAIL_RENAMING:
throw std::runtime_error((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
throw Slic3r::RuntimeError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
break;
case FAIL_CHECK_ORIGIN_NOT_OPENED:
throw std::runtime_error((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str());
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str());
break;
case FAIL_CHECK_TARGET_NOT_OPENED:
throw std::runtime_error((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
break;
default:
BOOST_LOG_TRIVIAL(warning) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << ".";
@ -210,7 +240,7 @@ void BackgroundSlicingProcess::thread_proc()
// Process the background slicing task.
m_state = STATE_RUNNING;
lck.unlock();
std::string error;
std::exception_ptr exception;
try {
assert(m_print != nullptr);
switch(m_print->technology()) {
@ -221,15 +251,8 @@ void BackgroundSlicingProcess::thread_proc()
} catch (CanceledException & /* ex */) {
// Canceled, this is all right.
assert(m_print->canceled());
} catch (const std::bad_alloc& ex) {
wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it."))) % SLIC3R_APP_NAME).str());
error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what());
} catch (std::exception &ex) {
error = ex.what();
} catch (...) {
error = "Unknown C++ exception.";
exception = std::current_exception();
}
m_print->finalize();
lck.lock();
@ -237,9 +260,9 @@ void BackgroundSlicingProcess::thread_proc()
if (m_print->cancel_status() != Print::CANCELED_INTERNAL) {
// Only post the canceled event, if canceled by user.
// Don't post the canceled event, if canceled from Print::apply().
wxCommandEvent evt(m_event_finished_id);
evt.SetString(GUI::from_u8(error));
evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0));
SlicingProcessCompletedEvent evt(m_event_finished_id, 0,
(m_state == STATE_CANCELED) ? SlicingProcessCompletedEvent::Cancelled :
exception ? SlicingProcessCompletedEvent::Error : SlicingProcessCompletedEvent::Finished, exception);
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
}
m_print->restart();
@ -299,7 +322,7 @@ bool BackgroundSlicingProcess::start()
// The background processing thread is already running.
return false;
if (! this->idle())
throw std::runtime_error("Cannot start a background task, the worker thread is not idle.");
throw Slic3r::RuntimeError("Cannot start a background task, the worker thread is not idle.");
m_state = STATE_STARTED;
m_print->set_cancel_callback([this](){ this->stop_internal(); });
lck.unlock();
@ -494,7 +517,7 @@ void BackgroundSlicingProcess::prepare_upload()
if (m_print == m_fff_print) {
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
if (copy_file(m_temp_output_path, source_path.string()) != SUCCESS) {
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
}
run_post_process_scripts(source_path.string(), m_fff_print->config());
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());

View file

@ -37,6 +37,36 @@ public:
PrintBase::SlicingStatus status;
};
class SlicingProcessCompletedEvent : public wxEvent
{
public:
enum StatusType {
Finished,
Cancelled,
Error
};
SlicingProcessCompletedEvent(wxEventType eventType, int winid, StatusType status, std::exception_ptr exception) :
wxEvent(winid, eventType), m_status(status), m_exception(exception) {}
virtual wxEvent* Clone() const { return new SlicingProcessCompletedEvent(*this); }
StatusType status() const { return m_status; }
bool finished() const { return m_status == Finished; }
bool success() const { return m_status == Finished; }
bool cancelled() const { return m_status == Cancelled; }
bool error() const { return m_status == Error; }
// Unhandled error produced by stdlib or a Win32 structured exception, or unhandled Slic3r's own critical exception.
bool critical_error() const;
// Only valid if error()
void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); }
// Produce a human readable message to be displayed by a notification or a message box.
std::string format_error_message() const;
private:
StatusType m_status;
std::exception_ptr m_exception;
};
wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent);
// Print step IDs for keeping track of the print state.

View file

@ -1,15 +1,15 @@
#include <exception>
namespace Slic3r {
class ConfigError : public std::runtime_error {
using std::runtime_error::runtime_error;
class ConfigError : public Slic3r::RuntimeError {
using Slic3r::RuntimeError::RuntimeError;
};
namespace GUI {
class ConfigGUITypeError : public ConfigError {
using ConfigError::ConfigError;
using ConfigError::ConfigError;
};
}
}
} // namespace GUI
} // namespace Slic3r

View file

@ -123,7 +123,7 @@ Bundle& BundleMap::prusa_bundle()
{
auto it = find(PresetBundle::PRUSA_BUNDLE);
if (it == end()) {
throw std::runtime_error("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded");
throw Slic3r::RuntimeError("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded");
}
return it->second;

View file

@ -766,7 +766,7 @@ const char* FirmwareDialog::priv::avr109_dev_name(Avr109Pid usb_pid) {
return "Original Prusa CW1";
break;
default: throw std::runtime_error((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str());
default: throw Slic3r::RuntimeError((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str());
}
}

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,9 @@ namespace GUI {
class GCodeViewer
{
using Color = std::array<float, 3>;
using IndexBuffer = std::vector<unsigned int>;
using MultiIndexBuffer = std::vector<IndexBuffer>;
static const std::vector<Color> Extrusion_Role_Colors;
static const std::vector<Color> Options_Colors;
static const std::vector<Color> Travel_Colors;
@ -112,10 +115,12 @@ class GCodeViewer
{
struct Endpoint
{
// index into the indices buffer
unsigned int i_id{ 0u };
// sequential id
unsigned int s_id{ 0u };
// index of the index buffer
unsigned int b_id{ 0 };
// index into the index buffer
size_t i_id{ 0 };
// sequential id (index into the vertex buffer)
size_t s_id{ 0 };
Vec3f position{ Vec3f::Zero() };
};
@ -134,16 +139,17 @@ class GCodeViewer
bool matches(const GCodeProcessor::MoveVertex& move) const;
size_t vertices_count() const { return last.s_id - first.s_id + 1; }
bool contains(unsigned int id) const { return first.s_id <= id && id <= last.s_id; }
bool contains(size_t id) const { return first.s_id <= id && id <= last.s_id; }
};
// Used to batch the indices needed to render paths
struct RenderPath
{
Color color;
size_t path_id;
unsigned int path_id;
unsigned int index_buffer_id;
std::vector<unsigned int> sizes;
std::vector<size_t> offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements())
std::vector<size_t> offsets; // use size_t because we need an unsigned int whose size matches pointer's size (used in the call glMultiDrawElements())
};
// buffer containing data for rendering a specific toolpath type
@ -158,7 +164,7 @@ class GCodeViewer
ERenderPrimitiveType render_primitive_type;
VBuffer vertices;
IBuffer indices;
std::vector<IBuffer> indices;
std::string shader;
std::vector<Path> paths;
@ -166,7 +172,10 @@ class GCodeViewer
bool visible{ false };
void reset();
void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id);
// b_id index of buffer contained in this->indices
// i_id index of first index contained in this->indices[b_id]
// s_id index of first vertex contained in this->vertices
void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
unsigned int indices_per_segment() const {
switch (render_primitive_type)
{
@ -194,6 +203,8 @@ class GCodeViewer
default: { return 0; }
}
}
bool has_data() const { return vertices.id != 0 && !indices.empty() && indices.front().id != 0; }
};
// helper to render shells
@ -264,7 +275,7 @@ class GCodeViewer
#if ENABLE_GCODE_VIEWER_STATISTICS
struct Statistics
{
// times
// time
long long results_time{ 0 };
long long load_time{ 0 };
long long refresh_time{ 0 };
@ -279,15 +290,17 @@ class GCodeViewer
long long indices_gpu_size{ 0 };
long long paths_size{ 0 };
long long render_paths_size{ 0 };
// others
// other
long long travel_segments_count{ 0 };
long long extrude_segments_count{ 0 };
long long max_vertices_in_vertex_buffer{ 0 };
long long max_indices_in_index_buffer{ 0 };
void reset_all() {
reset_times();
reset_opengl();
reset_sizes();
reset_counters();
reset_others();
}
void reset_times() {
@ -311,9 +324,11 @@ class GCodeViewer
render_paths_size = 0;
}
void reset_counters() {
void reset_others() {
travel_segments_count = 0;
extrude_segments_count = 0;
max_vertices_in_vertex_buffer = 0;
max_indices_in_index_buffer = 0;
}
};
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -346,8 +361,8 @@ public:
struct Endpoints
{
unsigned int first{ 0 };
unsigned int last{ 0 };
size_t first{ 0 };
size_t last{ 0 };
};
Endpoints endpoints;
@ -371,7 +386,7 @@ public:
private:
unsigned int m_last_result_id{ 0 };
size_t m_vertices_count{ 0 };
size_t m_moves_count{ 0 };
mutable std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) };
// bounding box of toolpaths
BoundingBoxf3 m_paths_bounding_box;
@ -444,7 +459,6 @@ public:
void export_toolpaths_to_obj(const char* filename) const;
private:
void init_shaders();
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
void load_shells(const Print& print, bool initialized);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
@ -466,6 +480,7 @@ private:
return in_z_range(path.first.position[2]) || in_z_range(path.last.position[2]);
}
bool is_travel_in_z_range(size_t id) const;
void log_memory_used(const std::string& label, long long additional = 0) const;
};
} // namespace GUI

View file

@ -4999,7 +4999,7 @@ bool GLCanvas3D::_init_main_toolbar()
return false;
item.name = "settings";
item.icon_filename = "cog_.svg";
item.icon_filename = "settings.svg";
item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") +
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) +
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ;
@ -5011,35 +5011,16 @@ bool GLCanvas3D::_init_main_toolbar()
if (!m_main_toolbar.add_item(item))
return false;
/*
if (!m_main_toolbar.add_separator())
return false;
item.name = "layersediting";
item.icon_filename = "layers_white.svg";
item.tooltip = _utf8(L("Variable layer height"));
item.sprite_id = 11;
item.left.toggable = true;
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
item.visibility_callback = [this]()->bool
{
bool res = m_process->current_printer_technology() == ptFFF;
// turns off if changing printer technology
if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting"))
force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting"));
return res;
};
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
if (!m_main_toolbar.add_item(item))
return false;
if (!m_main_toolbar.add_separator())
return false;
*/
item.name = "search";
item.icon_filename = "search_.svg";
item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]";
item.sprite_id = 12;
item.sprite_id = 11;
item.left.toggable = true;
item.left.render_callback = [this](float left, float right, float, float) {
if (m_canvas != nullptr)
{
@ -5053,6 +5034,27 @@ bool GLCanvas3D::_init_main_toolbar()
if (!m_main_toolbar.add_item(item))
return false;
if (!m_main_toolbar.add_separator())
return false;
item.name = "layersediting";
item.icon_filename = "layers_white.svg";
item.tooltip = _utf8(L("Variable layer height"));
item.sprite_id = 12;
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
item.visibility_callback = [this]()->bool {
bool res = m_process->current_printer_technology() == ptFFF;
// turns off if changing printer technology
if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting"))
force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting"));
return res;
};
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
item.left.render_callback = GLToolbarItem::Default_Render_Callback;
if (!m_main_toolbar.add_item(item))
return false;
return true;
}
@ -5161,10 +5163,10 @@ bool GLCanvas3D::_init_undoredo_toolbar()
if (!m_undoredo_toolbar.add_item(item))
return false;
/*
if (!m_undoredo_toolbar.add_separator())
return false;
*/
return true;
}
@ -5553,6 +5555,10 @@ void GLCanvas3D::_render_selection_center() const
void GLCanvas3D::_check_and_update_toolbar_icon_scale() const
{
// Don't update a toolbar scale, when we are on a Preview
if (wxGetApp().plater()->is_preview_shown())
return;
float scale = wxGetApp().toolbar_icon_scale();
Size cnv_size = get_canvas_size();

View file

@ -211,7 +211,7 @@ public:
copyright_string += //"Slic3r" + _L("is licensed under the") + _L("GNU Affero General Public License, version 3") + "\n\n" +
_L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" +
_L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others.") + "\n\n" +
_L("Splash screen could be desabled from the \"Preferences\"");
_L("Splash screen can be disabled from the \"Preferences\"");
word_wrap_string(copyright_string, banner_width, screen_scale);
@ -599,7 +599,7 @@ void GUI_App::init_app_config()
std::string error = app_config->load();
if (!error.empty())
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
throw std::runtime_error(
throw Slic3r::RuntimeError(
_u8L("Error parsing PrusaSlicer config file, it is probably corrupted. "
"Try to manually delete the file to recover from the error. Your user profiles will not be affected.") +
"\n\n" + AppConfig::config_path() + "\n\n" + error);

View file

@ -927,7 +927,7 @@ void ImGuiWrapper::init_font(bool compress)
if (font == nullptr) {
font = io.Fonts->AddFontDefault();
if (font == nullptr) {
throw std::runtime_error("ImGui: Could not load deafult font");
throw Slic3r::RuntimeError("ImGui: Could not load deafult font");
}
}

View file

@ -7,6 +7,7 @@
#include <wx/numformatter.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include "libslic3r/Exception.hpp"
#include "libslic3r/Utils.hpp"
#include "I18N.hpp"
@ -64,7 +65,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
break;
case coNone: break;
default:
throw /*//!ConfigGUITypeError("")*/std::logic_error("This control doesn't exist till now"); break;
throw Slic3r::LogicError("This control doesn't exist till now"); break;
}
}
// Grab a reference to fields for convenience
@ -683,7 +684,7 @@ boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_
// Aggregate the strings the old way.
// Currently used for the post_process config value only.
if (opt_index != -1)
throw std::out_of_range("Can't deserialize option indexed value");
throw Slic3r::OutOfRange("Can't deserialize option indexed value");
// return join(';', m_config->get(opt_key)});
return get_config_value(*m_config, opt_key);
}

View file

@ -107,7 +107,7 @@ namespace GUI {
wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent);
wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent);
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent);
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, SlicingProcessCompletedEvent);
wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
// Sidebar widgets
@ -1168,8 +1168,27 @@ void Sidebar::update_sliced_info_sizer()
p->sliced_info->SetTextAndShow(siCost, info_text, new_label);
#if ENABLE_GCODE_VIEWER
// hide the estimate time
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A")
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
else {
info_text = "";
new_label = _L("Estimated printing time") + ":";
if (ps.estimated_normal_print_time != "N/A") {
new_label += format_wxstr("\n - %1%", _L("normal mode"));
info_text += format_wxstr("\n%1%", short_time(ps.estimated_normal_print_time));
// uncomment next line to not disappear slicing finished notif when colapsing sidebar before time estimate
//if (p->plater->is_sidebar_collapsed())
p->plater->get_notification_manager()->set_slicing_complete_large(p->plater->is_sidebar_collapsed());
p->plater->get_notification_manager()->set_slicing_complete_print_time("Estimated printing time: " + ps.estimated_normal_print_time);
}
if (ps.estimated_silent_print_time != "N/A") {
new_label += format_wxstr("\n - %1%", _L("stealth mode"));
info_text += format_wxstr("\n%1%", short_time(ps.estimated_silent_print_time));
}
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
}
#else
if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A")
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
@ -1684,7 +1703,7 @@ struct Plater::priv
void on_select_preset(wxCommandEvent&);
void on_slicing_update(SlicingStatusEvent&);
void on_slicing_completed(wxCommandEvent&);
void on_process_completed(wxCommandEvent&);
void on_process_completed(SlicingProcessCompletedEvent&);
void on_export_began(wxCommandEvent&);
void on_layer_editing_toggled(bool enable);
void on_slicing_began();
@ -3512,7 +3531,7 @@ bool Plater::priv::warnings_dialog()
return res == wxID_OK;
}
void Plater::priv::on_process_completed(wxCommandEvent &evt)
void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
{
// Stop the background task, wait until the thread goes into the "Idle" state.
// At this point of time the thread should be either finished or canceled,
@ -3521,27 +3540,30 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
this->statusbar()->reset_cancel_callback();
this->statusbar()->stop_busy();
const bool canceled = evt.GetInt() < 0;
const bool error = evt.GetInt() == 0;
const bool success = evt.GetInt() > 0;
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
this->background_process.reset_export();
if (error) {
wxString message = evt.GetString();
if (message.IsEmpty())
message = _L("Export failed.");
notification_manager->push_slicing_error_notification(boost::nowide::narrow(message), *q->get_current_canvas3D());
this->statusbar()->set_status_text(message);
if (evt.error()) {
std::string message = evt.format_error_message();
if (evt.critical_error()) {
if (q->m_tracking_popup_menu)
// We don't want to pop-up a message box when tracking a pop-up menu.
// We postpone the error message instead.
q->m_tracking_popup_menu_error_message = message;
else
show_error(q, message);
} else
notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D());
this->statusbar()->set_status_text(from_u8(message));
const wxString invalid_str = _L("Invalid data");
for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport })
sidebar->set_btn_label(btn, invalid_str);
process_completed_with_error = true;
}
if (canceled)
if (evt.cancelled())
this->statusbar()->set_status_text(_L("Cancelled"));
this->sidebar->show_sliced_info_sizer(success);
this->sidebar->show_sliced_info_sizer(evt.success());
// This updates the "Slice now", "Export G-code", "Arrange" buttons status.
// Namely, it refreshes the "Out of print bed" property of all the ModelObjects, and it enables
@ -3562,7 +3584,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
default: break;
}
if (canceled) {
if (evt.cancelled()) {
if (wxGetApp().get_mode() == comSimple)
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
show_action_buttons(true);
@ -5378,6 +5400,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
// print technology is changed, so we should to update a search list
p->sidebar->update_searcher();
p->sidebar->show_sliced_info_sizer(false);
#if ENABLE_GCODE_VIEWER
p->reset_gcode_toolpaths();
#endif // ENABLE_GCODE_VIEWER

View file

@ -741,7 +741,8 @@ void PlaterPresetComboBox::update()
if (m_type == Preset::TYPE_FILAMENT)
{
// Assign an extruder color to the selected item if the extruder color is defined.
filament_rgb = preset.config.opt_string("filament_colour", 0);
filament_rgb = is_selected ? selected_filament_preset->config.opt_string("filament_colour", 0) :
preset.config.opt_string("filament_colour", 0);
extruder_rgb = (is_selected && !extruder_color.empty()) ? extruder_color : filament_rgb;
single_bar = filament_rgb == extruder_rgb;

View file

@ -140,8 +140,6 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
{
const auto em = GetTextExtent("m").x;
SetSize(wxSize(HEIGHT * em, WIDTH * em));
auto *topsizer = new wxBoxSizer(wxVERTICAL);
job_list = new wxDataViewListCtrl(this, wxID_ANY);
@ -168,6 +166,8 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
topsizer->Add(btnsizer, 0, wxEXPAND);
SetSizer(topsizer);
SetSize(wxSize(HEIGHT * em, WIDTH * em));
job_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent&) { on_list_select(); });
btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {

View file

@ -590,8 +590,11 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
int btn_idx = 0;
add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++);
if (dependent_presets && (type != dependent_presets->type() ? true :
dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology()))
const PresetCollection& printers = wxGetApp().preset_bundle->printers;
if (dependent_presets && (type == dependent_presets->type() ?
dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
printers.get_edited_preset().printer_technology() == printers.find_preset(new_selected_preset)->printer_technology() ) )
add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++);
add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false);
@ -935,7 +938,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
}
for (const std::string& opt_key : /*presets->current_dirty_options()*/dirty_options) {
for (const std::string& opt_key : dirty_options) {
const Search::Option& option = searcher.get_option(opt_key);
ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type };

View file

@ -436,7 +436,7 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in,
if (bmp == nullptr) {
// Neither SVG nor PNG has been found, raise error
throw std::runtime_error("Could not load bitmap: " + bmp_name);
throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name);
}
return *bmp;

View file

@ -338,7 +338,7 @@ class BlinkingBitmap : public wxStaticBitmap
{
public:
BlinkingBitmap() {};
BlinkingBitmap(wxWindow* parent, const std::string& icon_name = "redo_toolbar");
BlinkingBitmap(wxWindow* parent, const std::string& icon_name = "search_blink");
~BlinkingBitmap() {}

View file

@ -209,10 +209,10 @@ typedef std::function<void (const char * /* message */, unsigned /* progress */)
void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel)
{
if (! is_windows10())
throw std::runtime_error("fix_model_by_win10_sdk called on non Windows 10 system");
throw Slic3r::RuntimeError("fix_model_by_win10_sdk called on non Windows 10 system");
if (! winrt_load_runtime_object_library())
throw std::runtime_error("Failed to initialize the WinRT library.");
throw Slic3r::RuntimeError("Failed to initialize the WinRT library.");
HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
{
@ -232,7 +232,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
if (status == AsyncStatus::Completed)
hr = modelAsync->GetResults(model.GetAddressOf());
else
throw std::runtime_error(L("Failed loading the input model."));
throw Slic3r::RuntimeError(L("Failed loading the input model."));
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes;
hr = model->get_Meshes(meshes.GetAddressOf());
@ -245,7 +245,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
hr = model->RepairAsync(repairAsync.GetAddressOf());
status = winrt_async_await(repairAsync, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Mesh repair failed."));
throw Slic3r::RuntimeError(L("Mesh repair failed."));
repairAsync->GetResults();
on_progress(L("Loading repaired model"), 60);
@ -260,14 +260,14 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf());
status = winrt_async_await(saveToPackageAsync, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
throw Slic3r::RuntimeError(L("Saving mesh into the 3MF container failed."));
hr = saveToPackageAsync->GetResults();
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync;
hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf());
status = winrt_async_await(generatorStreamAsync, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
throw Slic3r::RuntimeError(L("Saving mesh into the 3MF container failed."));
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream;
hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf());
@ -299,7 +299,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf());
status = winrt_async_await(asyncRead, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
throw Slic3r::RuntimeError(L("Saving mesh into the 3MF container failed."));
hr = buffer->get_Length(&length);
if (length == 0)
break;
@ -365,7 +365,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
model_object->add_instance();
if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) {
boost::filesystem::remove(path_src);
throw std::runtime_error(L("Export of a temporary 3mf file failed"));
throw Slic3r::RuntimeError(L("Export of a temporary 3mf file failed"));
}
model.clear_objects();
model.clear_materials();
@ -380,15 +380,15 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &config, &model, false);
boost::filesystem::remove(path_dst);
if (! loaded)
throw std::runtime_error(L("Import of the repaired 3mf file failed"));
throw Slic3r::RuntimeError(L("Import of the repaired 3mf file failed"));
if (model.objects.size() == 0)
throw std::runtime_error(L("Repaired 3MF file does not contain any object"));
throw Slic3r::RuntimeError(L("Repaired 3MF file does not contain any object"));
if (model.objects.size() > 1)
throw std::runtime_error(L("Repaired 3MF file contains more than one object"));
throw Slic3r::RuntimeError(L("Repaired 3MF file contains more than one object"));
if (model.objects.front()->volumes.size() == 0)
throw std::runtime_error(L("Repaired 3MF file does not contain any volume"));
throw Slic3r::RuntimeError(L("Repaired 3MF file does not contain any volume"));
if (model.objects.front()->volumes.size() > 1)
throw std::runtime_error(L("Repaired 3MF file contains more than one volume"));
throw Slic3r::RuntimeError(L("Repaired 3MF file contains more than one volume"));
meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh()));
}
for (size_t i = 0; i < volumes.size(); ++ i) {

View file

@ -156,7 +156,7 @@ Http::priv::priv(const std::string &url)
Http::tls_global_init();
if (curl == nullptr) {
throw std::runtime_error(std::string("Could not construct Curl object"));
throw Slic3r::RuntimeError(std::string("Could not construct Curl object"));
}
set_timeout_connect(DEFAULT_TIMEOUT_CONNECT);

View file

@ -11,7 +11,6 @@
#include <wx/progdlg.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "Http.hpp"

View file

@ -6,6 +6,7 @@
#include <boost/optional.hpp>
#include "PrintHost.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {

View file

@ -2,6 +2,7 @@
#define GUI_PROCESS_HPP
class wxWindow;
class wxString;
namespace Slic3r {
namespace GUI {

View file

@ -1,5 +1,7 @@
#include "Serial.hpp"
#include "libslic3r/Exception.hpp"
#include <algorithm>
#include <string>
#include <vector>
@ -298,7 +300,7 @@ void Serial::set_baud_rate(unsigned baud_rate)
auto handle_errno = [](int retval) {
if (retval != 0) {
throw std::runtime_error(
throw Slic3r::RuntimeError(
(boost::format("Could not set baud rate: %1%") % strerror(errno)).str()
);
}
@ -346,7 +348,7 @@ void Serial::set_baud_rate(unsigned baud_rate)
handle_errno(::cfsetspeed(&ios, baud_rate));
handle_errno(::tcsetattr(handle, TCSAFLUSH, &ios));
#else
throw std::runtime_error("Custom baud rates are not currently supported on this OS");
throw Slic3r::RuntimeError("Custom baud rates are not currently supported on this OS");
#endif
}
}
@ -358,7 +360,7 @@ void Serial::set_DTR(bool on)
auto handle = native_handle();
#if defined(_WIN32) && !defined(__SYMBIAN32__)
if (! EscapeCommFunction(handle, on ? SETDTR : CLRDTR)) {
throw std::runtime_error("Could not set serial port DTR");
throw Slic3r::RuntimeError("Could not set serial port DTR");
}
#else
int status;
@ -369,7 +371,7 @@ void Serial::set_DTR(bool on)
}
}
throw std::runtime_error(
throw Slic3r::RuntimeError(
(boost::format("Could not set serial port DTR: %1%") % strerror(errno)).str()
);
#endif

View file

@ -847,7 +847,7 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU
// Find the snapshot by time. It must exist.
const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp));
if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp)
throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str());
throw Slic3r::RuntimeError((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str());
m_active_snapshot_time = timestamp;
model.clear_objects();